diff options
Diffstat (limited to 'doc/libmicrohttpd-tutorial.info')
-rw-r--r-- | doc/libmicrohttpd-tutorial.info | 4231 |
1 files changed, 4231 insertions, 0 deletions
diff --git a/doc/libmicrohttpd-tutorial.info b/doc/libmicrohttpd-tutorial.info new file mode 100644 index 00000000..79632a40 --- /dev/null +++ b/doc/libmicrohttpd-tutorial.info @@ -0,0 +1,4231 @@ +This is libmicrohttpd-tutorial.info, produced by makeinfo version 4.13 +from libmicrohttpd-tutorial.texi. + +INFO-DIR-SECTION Software libraries +START-INFO-DIR-ENTRY +* libmicrohttpdtutorial: (libmicrohttpd). A tutorial for GNU libmicrohttpd. +END-INFO-DIR-ENTRY + + This tutorial documents GNU libmicrohttpd version 0.9.23, last +updated 17 November 2013. + + Copyright (c) 2008 Sebastian Gerhardt. + + Copyright (c) 2010, 2011, 2012, 2013 Christian Grothoff. + + Permission is granted to copy, distribute and/or modify this + document under the terms of the GNU Free Documentation License, + Version 1.3 or any later version published by the Free Software + Foundation; with no Invariant Sections, no Front-Cover Texts, and + no Back-Cover Texts. A copy of the license is included in the + section entitled "GNU Free Documentation License". + + +File: libmicrohttpd-tutorial.info, Node: Top, Next: Introduction, Up: (dir) + +A Tutorial for GNU libmicrohttpd +******************************** + +This tutorial documents GNU libmicrohttpd version 0.9.23, last updated +17 November 2013. + + Copyright (c) 2008 Sebastian Gerhardt. + + Copyright (c) 2010, 2011, 2012, 2013 Christian Grothoff. + + Permission is granted to copy, distribute and/or modify this + document under the terms of the GNU Free Documentation License, + Version 1.3 or any later version published by the Free Software + Foundation; with no Invariant Sections, no Front-Cover Texts, and + no Back-Cover Texts. A copy of the license is included in the + section entitled "GNU Free Documentation License". + +* Menu: + +* Introduction:: +* Hello browser example:: +* Exploring requests:: +* Response headers:: +* Supporting basic authentication:: +* Processing POST data:: +* Improved processing of POST data:: +* Session management:: +* Adding a layer of security:: +* Bibliography:: +* License text:: +* Example programs:: + + +File: libmicrohttpd-tutorial.info, Node: Introduction, Next: Hello browser example, Prev: Top, Up: Top + +1 Introduction +************** + +This tutorial is for developers who want to learn how they can add HTTP +serving capabilities to their applications with the _GNU libmicrohttpd_ +library, abbreviated _MHD_. The reader will learn how to implement +basic HTTP functions from simple executable sample programs that +implement various features. + + The text is supposed to be a supplement to the API reference manual +of _GNU libmicrohttpd_ and for that reason does not explain many of the +parameters. Therefore, the reader should always consult the manual to +find the exact meaning of the functions used in the tutorial. +Furthermore, the reader is encouraged to study the relevant _RFCs_, +which document the HTTP standard. + + _GNU libmicrohttpd_ is assumed to be already installed. This +tutorial is written for version 0.9.23. At the time being, this +tutorial has only been tested on _GNU/Linux_ machines even though +efforts were made not to rely on anything that would prevent the +samples from being built on similar systems. + +1.1 History +=========== + +This tutorial was originally written by Sebastian Gerhardt for MHD +0.4.0. It was slighly polished and updated to MHD 0.9.0 by Christian +Grothoff. + + +File: libmicrohttpd-tutorial.info, Node: Hello browser example, Next: Exploring requests, Prev: Introduction, Up: Top + +2 Hello browser example +*********************** + +The most basic task for a HTTP server is to deliver a static text +message to any client connecting to it. Given that this is also easy +to implement, it is an excellent problem to start with. + + For now, the particular URI the client asks for shall have no effect +on the message that will be returned. In addition, the server shall end +the connection after the message has been sent so that the client will +know there is nothing more to expect. + + The C program `hellobrowser.c', which is to be found in the examples +section, does just that. If you are very eager, you can compile and +start it right away but it is advisable to type the lines in by +yourself as they will be discussed and explained in detail. + + After the necessary includes and the definition of the port which +our server should listen on +#include <sys/types.h> +#include <sys/select.h> +#include <sys/socket.h> +#include <microhttpd.h> + +#define PORT 8888 + +the desired behaviour of our server when HTTP request arrive has to be +implemented. We already have agreed that it should not care about the +particular details of the request, such as who is requesting what. The +server will respond merely with the same small HTML page to every +request. + + The function we are going to write now will be called by _GNU +libmicrohttpd_ every time an appropriate request comes in. While the +name of this callback function is arbitrary, its parameter list has to +follow a certain layout. So please, ignore the lot of parameters for +now, they will be explained at the point they are needed. We have to +use only one of them, `struct MHD_Connection *connection', for the +minimalistic functionality we want to archive at the moment. + + This parameter is set by the _libmicrohttpd_ daemon and holds the +necessary information to relate the call with a certain connection. +Keep in mind that a server might have to satisfy hundreds of concurrent +connections and we have to make sure that the correct data is sent to +the destined client. Therefore, this variable is a means to refer to a +particular connection if we ask the daemon to sent the reply. + + Talking about the reply, it is defined as a string right after the +function header +int answer_to_connection (void *cls, struct MHD_Connection *connection, + const char *url, + const char *method, const char *version, + const char *upload_data, + size_t *upload_data_size, void **con_cls) +{ + const char *page = "<html><body>Hello, browser!</body></html>"; + +HTTP is a rather strict protocol and the client would certainly +consider it "inappropriate" if we just sent the answer string "as is". +Instead, it has to be wrapped with additional information stored in +so-called headers and footers. Most of the work in this area is done +by the library for us--we just have to ask. Our reply string packed in +the necessary layers will be called a "response". To obtain such a +response we hand our data (the reply-string) and its size over to the +`MHD_create_response_from_buffer' function. The last two parameters +basically tell _MHD_ that we do not want it to dispose the message data +for us when it has been sent and there also needs no internal copy to +be done because the _constant_ string won't change anyway. + + struct MHD_Response *response; + int ret; + + response = MHD_create_response_from_buffer (strlen (page), + (void*) page, MHD_RESPMEM_PERSISTENT); + +Now that the the response has been laced up, it is ready for delivery +and can be queued for sending. This is done by passing it to another +_GNU libmicrohttpd_ function. As all our work was done in the scope of +one function, the recipient is without doubt the one associated with the +local variable `connection' and consequently this variable is given to +the queue function. Every HTTP response is accompanied by a status +code, here "OK", so that the client knows this response is the intended +result of his request and not due to some error or malfunction. + + Finally, the packet is destroyed and the return value from the queue +returned, already being set at this point to either MHD_YES or MHD_NO +in case of success or failure. + + ret = MHD_queue_response (connection, MHD_HTTP_OK, response); + MHD_destroy_response (response); + + return ret; +} + +With the primary task of our server implemented, we can start the +actual server daemon which will listen on `PORT' for connections. This +is done in the main function. +int main () +{ + struct MHD_Daemon *daemon; + + daemon = MHD_start_daemon (MHD_USE_SELECT_INTERNALLY, PORT, NULL, NULL, + &answer_to_connection, NULL, MHD_OPTION_END); + if (NULL == daemon) return 1; + +The first parameter is one of three possible modes of operation. Here +we want the daemon to run in a separate thread and to manage all +incoming connections in the same thread. This means that while +producing the response for one connection, the other connections will +be put on hold. In this example, where the reply is already known and +therefore the request is served quickly, this poses no problem. + + We will allow all clients to connect regardless of their name or +location, therefore we do not check them on connection and set the +forth and fifth parameter to NULL. + + Parameter six is the address of the function we want to be called +whenever a new connection has been established. Our +`answer_to_connection' knows best what the client wants and needs no +additional information (which could be passed via the next parameter) +so the next parameter is NULL. Likewise, we do not need to pass extra +options to the daemon so we just write the MHD_OPTION_END as the last +parameter. + + As the server daemon runs in the background in its own thread, the +execution flow in our main function will contine right after the call. +Because of this, we must delay the execution flow in the main thread or +else the program will terminate prematurely. We let it pause in a +processing-time friendly manner by waiting for the enter key to be +pressed. In the end, we stop the daemon so it can do its cleanup tasks. + getchar (); + + MHD_stop_daemon (daemon); + return 0; +} + +The first example is now complete. + + Compile it with +cc hellobrowser.c -o hellobrowser -I$PATH_TO_LIBMHD_INCLUDES + -L$PATH_TO_LIBMHD_LIBS -lmicrohttpd + with the two paths set accordingly and run it. + + Now open your favorite Internet browser and go to the address +`http://localhost:8888/', provided that 8888 is the port you chose. If +everything works as expected, the browser will present the message of +the static HTML page it got from our minimal server. + +Remarks +======= + +To keep this first example as small as possible, some drastic shortcuts +were taken and are to be discussed now. + + Firstly, there is no distinction made between the kinds of requests +a client could send. We implied that the client sends a GET request, +that means, that he actually asked for some data. Even when it is not +intended to accept POST requests, a good server should at least +recognize that this request does not constitute a legal request and +answer with an error code. This can be easily implemented by checking +if the parameter `method' equals the string "GET" and returning a +`MHD_NO' if not so. + + Secondly, the above practice of queuing a response upon the first +call of the callback function brings with it some limitations. This is +because the content of the message body will not be received if a +response is queued in the first iteration. Furthermore, the connection +will be closed right after the response has been transferred then. +This is typically not what you want as it disables HTTP pipelining. +The correct approach is to simply not queue a message on the first +callback unless there is an error. The `void**' argument to the +callback provides a location for storing information about the history +of the connection; for the first call, the pointer will point to NULL. +A simplistic way to differenciate the first call from others is to check +if the pointer is NULL and set it to a non-NULL value during the first +call. + + Both of these issues you will find addressed in the official +`minimal_example.c' residing in the `src/examples' directory of the +_MHD_ package. The source code of this program should look very +familiar to you by now and easy to understand. + + For our example, the `must_copy' and `must_free' parameter at the +response construction function could be set to `MHD_NO'. In the usual +case, responses cannot be sent immediately after being queued. For +example, there might be other data on the system that needs to be sent +with a higher priority. Nevertheless, the queue function will return +successfully--raising the problem that the data we have pointed to may +be invalid by the time it is about being sent. This is not an issue +here because we can expect the `page' string, which is a constant +_string literal_ here, to be static. That means it will be present and +unchanged for as long as the program runs. For dynamic data, one could +choose to either have _MHD_ free the memory `page' points to itself +when it is not longer needed or, alternatively, have the library to +make and manage its own copy of it. + +Exercises +========= + + * While the server is running, use a program like `telnet' or + `netcat' to connect to it. Try to form a valid HTTP 1.1 request + yourself like GET /dontcare HTTP/1.1 + Host: itsme + <enter> + and see what the server returns to you. + + * Also, try other requests, like POST, and see how our server does + not mind and why. How far in malforming a request can you go + before the builtin functionality of _MHD_ intervenes and an + altered response is sent? Make sure you read about the status + codes in the _RFC_. + + * Add the option `MHD_USE_PEDANTIC_CHECKS' to the start function of + the daemon in `main'. Mind the special format of the parameter + list here which is described in the manual. How indulgent is the + server now to your input? + + * Let the main function take a string as the first command line + argument and pass `argv[1]' to the `MHD_start_daemon' function as + the sixth parameter. The address of this string will be passed to + the callback function via the `cls' variable. Decorate the text + given at the command line when the server is started with proper + HTML tags and send it as the response instead of the former static + string. + + * _Demanding:_ Write a separate function returning a string + containing some useful information, for example, the time. Pass + the function's address as the sixth parameter and evaluate this + function on every request anew in `answer_to_connection'. Remember + to free the memory of the string every time after satisfying the + request. + + + +File: libmicrohttpd-tutorial.info, Node: Exploring requests, Next: Response headers, Prev: Hello browser example, Up: Top + +3 Exploring requests +******************** + +This chapter will deal with the information which the client sends to +the server at every request. We are going to examine the most useful +fields of such an request and print them out in a readable manner. This +could be useful for logging facilities. + + The starting point is the _hellobrowser_ program with the former +response removed. + + This time, we just want to collect information in the callback +function, thus we will just return MHD_NO after we have probed the +request. This way, the connection is closed without much ado by the +server. + +static int +answer_to_connection (void *cls, struct MHD_Connection *connection, + const char *url, + const char *method, const char *version, + const char *upload_data, + size_t *upload_data_size, void **con_cls) +{ + ... + return MHD_NO; +} + The ellipsis marks the position where the following instructions shall +be inserted. + + We begin with the most obvious information available to the server, +the request line. You should already have noted that a request consists +of a command (or "HTTP method") and a URI (e.g. a filename). It also +contains a string for the version of the protocol which can be found in +`version'. To call it a "new request" is justified because we return +only `MHD_NO', thus ensuring the function will not be called again for +this connection. +printf ("New %s request for %s using version %s\n", method, url, version); + The rest of the information is a bit more hidden. Nevertheless, there +is lot of it sent from common Internet browsers. It is stored in +"key-value" pairs and we want to list what we find in the header. As +there is no mandatory set of keys a client has to send, each key-value +pair is printed out one by one until there are no more left. We do this +by writing a separate function which will be called for each pair just +like the above function is called for each HTTP request. It can then +print out the content of this pair. +int print_out_key (void *cls, enum MHD_ValueKind kind, + const char *key, const char *value) +{ + printf ("%s: %s\n", key, value); + return MHD_YES; +} + To start the iteration process that calls our new function for every +key, the line +MHD_get_connection_values (connection, MHD_HEADER_KIND, &print_out_key, NULL); + needs to be inserted in the connection callback function too. The +second parameter tells the function that we are only interested in keys +from the general HTTP header of the request. Our iterating function +`print_out_key' does not rely on any additional information to fulfill +its duties so the last parameter can be NULL. + + All in all, this constitutes the complete `logging.c' program for +this chapter which can be found in the `examples' section. + + Connecting with any modern Internet browser should yield a handful +of keys. You should try to interpret them with the aid of _RFC 2616_. +Especially worth mentioning is the "Host" key which is often used to +serve several different websites hosted under one single IP address but +reachable by different domain names (this is called virtual hosting). + +Conclusion +========== + +The introduced capabilities to itemize the content of a simple GET +request--especially the URI--should already allow the server to satisfy +clients' requests for small specific resources (e.g. files) or even +induce alteration of server state. However, the latter is not +recommended as the GET method (including its header data) is by +convention considered a "safe" operation, which should not change the +server's state in a significant way. By convention, GET operations can +thus be performed by crawlers and other automatic software. Naturally +actions like searching for a passed string are fine. + + Of course, no transmission can occur while the return value is still +set to `MHD_NO' in the callback function. + +Exercises +========= + + * By parsing the `url' string and delivering responses accordingly, + implement a small server for "virtual" files. When asked for + `/index.htm{l}', let the response consist of a HTML page + containing a link to `/another.html' page which is also to be + created "on the fly" in case of being requested. If neither of + these two pages are requested, `MHD_HTTP_NOT_FOUND' shall be + returned accompanied by an informative message. + + * A very interesting information has still been ignored by our + logger--the client's IP address. Implement a callback function static int on_client_connect (void *cls, + const struct sockaddr *addr, + socklen_t addrlen) + that prints out the IP address in an appropriate format. You + might want to use the POSIX function `inet_ntoa' but bear in mind + that `addr' is actually just a structure containing other + substructures and is _not_ the variable this function expects. + Make sure to return `MHD_YES' so that the library knows the client + is allowed to connect (and to then process the request). If one + wanted to limit access basing on IP addresses, this would be the + place to do it. The address of your `on_client_connect' function + must be passed as the third parameter to the `MHD_start_daemon' + call. + + + +File: libmicrohttpd-tutorial.info, Node: Response headers, Next: Supporting basic authentication, Prev: Exploring requests, Up: Top + +4 Response headers +****************** + +Now that we are able to inspect the incoming request in great detail, +this chapter discusses the means to enrich the outgoing responses +likewise. + + As you have learned in the _Hello, Browser_ chapter, some obligatory +header fields are added and set automatically for simple responses by +the library itself but if more advanced features are desired, +additional fields have to be created. One of the possible fields is +the content type field and an example will be developed around it. +This will lead to an application capable of correctly serving different +types of files. + + When we responded with HTML page packed in the static string +previously, the client had no choice but guessing about how to handle +the response, because the server had not told him. What if we had sent +a picture or a sound file? Would the message have been understood or +merely been displayed as an endless stream of random characters in the +browser? This is what the mime content types are for. The header of +the response is extended by certain information about how the data is +to be interpreted. + + To introduce the concept, a picture of the format _PNG_ will be sent +to the client and labeled accordingly with `image/png'. Once again, we +can base the new example on the `hellobrowser' program. + +#define FILENAME "picture.png" +#define MIMETYPE "image/png" + +static int +answer_to_connection (void *cls, struct MHD_Connection *connection, + const char *url, + const char *method, const char *version, + const char *upload_data, + size_t *upload_data_size, void **con_cls) +{ + unsigned char *buffer = NULL; + struct MHD_Response *response; + We want the program to open the file for reading and determine its +size: + int fd; + int ret; + struct stat sbuf; + + if (0 != strcmp (method, "GET")) + return MHD_NO; + if ( (-1 == (fd = open (FILENAME, O_RDONLY))) || + (0 != fstat (fd, &sbuf)) ) + { + /* error accessing file */ + /* ... (see below) */ + } + /* ... (see below) */ + When dealing with files, there is a lot that could go wrong on the +server side and if so, the client should be informed with +`MHD_HTTP_INTERNAL_SERVER_ERROR'. + + /* error accessing file */ + if (fd != -1) close (fd); + const char *errorstr = + "<html><body>An internal server error has occured!\ + </body></html>"; + response = + MHD_create_response_from_buffer (strlen (errorstr), + (void *) errorstr, + MHD_RESPMEM_PERSISTENT); + if (response) + { + ret = + MHD_queue_response (connection, MHD_HTTP_INTERNAL_SERVER_ERROR, + response); + MHD_destroy_response (response); + + return MHD_YES; + } + else + return MHD_NO; + if (!ret) + { + const char *errorstr = "<html><body>An internal server error has occured!\ + </body></html>"; + + if (buffer) free(buffer); + + response = MHD_create_response_from_buffer (strlen(errorstr), (void*) errorstr, + MHD_RESPMEM_PERSISTENT); + + if (response) + { + ret = MHD_queue_response (connection, + MHD_HTTP_INTERNAL_SERVER_ERROR, + response); + MHD_destroy_response (response); + + return MHD_YES; + } + else return MHD_NO; + } + Note that we nevertheless have to create a response object even for +sending a simple error code. Otherwise, the connection would just be +closed without comment, leaving the client curious about what has +happened. + + But in the case of success a response will be constructed directly +from the file descriptor: + + /* error accessing file */ + /* ... (see above) */ + } + + response = + MHD_create_response_from_fd_at_offset (sbuf.st_size, fd, 0); + MHD_add_response_header (response, "Content-Type", MIMETYPE); + ret = MHD_queue_response (connection, MHD_HTTP_OK, response); + MHD_destroy_response (response); + Note that the response object will take care of closing the file +desciptor for us. + + Up to this point, there was little new. The actual novelty is that +we enhance the header with the meta data about the content. Aware of +the field's name we want to add, it is as easy as that: +MHD_add_response_header(response, "Content-Type", MIMETYPE); + We do not have to append a colon expected by the protocol behind the +first field--_GNU libhttpdmicro_ will take care of this. + + The function finishes with the well-known lines + ret = MHD_queue_response (connection, MHD_HTTP_OK, response); + MHD_destroy_response (response); + return ret; +} + The complete program `responseheaders.c' is in the `examples' section +as usual. Find a _PNG_ file you like and save it to the directory the +example is run from under the name `picture.png'. You should find the +image displayed on your browser if everything worked well. + +Remarks +======= + +The include file of the _MHD_ library comes with the header types +mentioned in _RFC 2616_ already defined as macros. Thus, we could have +written `MHD_HTTP_HEADER_CONTENT_TYPE' instead of `"Content-Type"' as +well. However, one is not limited to these standard headers and could +add custom response headers without violating the protocol. Whether, +and how, the client would react to these custom header is up to the +receiver. Likewise, the client is allowed to send custom request +headers to the server as well, opening up yet more possibilities how +client and server could communicate with each other. + + The method of creating the response from a file on disk only works +for static content. Serving dynamically created responses will be a +topic of a future chapter. + +Exercises +========= + + * Remember that the original program was written under a few + assumptions--a static response using a local file being one of + them. In order to simulate a very large or hard to reach file that + cannot be provided instantly, postpone the queuing in the callback + with the `sleep' function for 30 seconds _if_ the file `/big.png' + is requested (but deliver the same as above). A request for + `/picture.png' should provide just the same but without any + artificial delays. + + Now start two instances of your browser (or even use two machines) + and see how the second client is put on hold while the first waits + for his request on the slow file to be fulfilled. + + Finally, change the sourcecode to use + `MHD_USE_THREAD_PER_CONNECTION' when the daemon is started and try + again. + + * Did you succeed in implementing the clock exercise yet? This time, + let the server save the program's start time `t' and implement a + response simulating a countdown that reaches 0 at `t+60'. + Returning a message saying on which point the countdown is, the + response should ultimately be to reply "Done" if the program has + been running long enough, + + An unofficial, but widely understood, response header line is + `Refresh: DELAY; url=URL' with the uppercase words substituted to + tell the client it should request the given resource after the + given delay again. Improve your program in that the browser (any + modern browser should work) automatically reconnects and asks for + the status again every 5 seconds or so. The URL would have to be + composed so that it begins with "http://", followed by the _URI_ + the server is reachable from the client's point of view. + + Maybe you want also to visualize the countdown as a status bar by + creating a `<table>' consisting of one row and `n' columns whose + fields contain small images of either a red or a green light. + + + +File: libmicrohttpd-tutorial.info, Node: Supporting basic authentication, Next: Processing POST data, Prev: Response headers, Up: Top + +5 Supporting basic authentication +********************************* + +With the small exception of IP address based access control, requests +from all connecting clients where served equally until now. This +chapter discusses a first method of client's authentication and its +limits. + + A very simple approach feasible with the means already discussed +would be to expect the password in the _URI_ string before granting +access to the secured areas. The password could be separated from the +actual resource identifier by a certain character, thus the request +line might look like +GET /picture.png?mypassword + In the rare situation where the client is customized enough and the +connection occurs through secured lines (e.g., a embedded device +directly attached to another via wire) and where the ability to embedd +a password in the URI or to pass on a URI with a password are desired, +this can be a reasonable choice. + + But when it is assumed that the user connecting does so with an +ordinary Internet browser, this implementation brings some problems +about. For example, the URI including the password stays in the address +field or at least in the history of the browser for anybody near enough +to see. It will also be inconvenient to add the password manually to +any new URI when the browser does not know how to compose this +automatically. + + At least the convenience issue can be addressed by employing the +simplest built-in password facilities of HTTP compliant browsers, hence +we want to start there. It will however turn out to have still severe +weaknesses in terms of security which need consideration. + + Before we will start implementing _Basic Authentication_ as +described in _RFC 2617_, we should finally abandon the bad practice of +responding every request the first time our callback is called for a +given connection. This is becoming more important now because the +client and the server will have to talk in a more bi-directional way +than before to + + But how can we tell whether the callback has been called before for +the particular connection? Initially, the pointer this parameter +references is set by _MHD_ in the callback. But it will also be +"remembered" on the next call (for the same connection). Thus, we will +generate no response until the parameter is non-null--implying the +callback was called before at least once. We do not need to share +information between different calls of the callback, so we can set the +parameter to any adress that is assured to be not null. The pointer to +the `connection' structure will be pointing to a legal address, so we +take this. + + The first time `answer_to_connection' is called, we will not even +look at the headers. + +static int +answer_to_connection (void *cls, struct MHD_Connection *connection, + const char *url, const char *method, const char *version, + const char *upload_data, size_t *upload_data_size, + void **con_cls) +{ + if (0 != strcmp(method, "GET")) return MHD_NO; + if (NULL == *con_cls) {*con_cls = connection; return MHD_YES;} + + ... + /* else respond accordingly */ + ... +} + Note how we lop off the connection on the first condition (no "GET" +request), but return asking for more on the other one with `MHD_YES'. +With this minor change, we can proceed to implement the actual +authentication process. + +Request for authentication +========================== + +Let us assume we had only files not intended to be handed out without +the correct username/password, so every "GET" request will be +challenged. _RFC 2617_ describes how the server shall ask for +authentication by adding a _WWW-Authenticate_ response header with the +name of the _realm_ protected. MHD can generate and queue such a +failure response for you using the `MHD_queue_basic_auth_fail_response' +API. The only thing you need to do is construct a response with the +error page to be shown to the user if he aborts basic authentication. +But first, you should check if the proper credentials were already +supplied using the `MHD_basic_auth_get_username_password' call. + + Your code would then look like this: +static int +answer_to_connection (void *cls, struct MHD_Connection *connection, + const char *url, const char *method, + const char *version, const char *upload_data, + size_t *upload_data_size, void **con_cls) +{ + char *user; + char *pass; + int fail; + struct MHD_Response *response; + + if (0 != strcmp (method, MHD_HTTP_METHOD_GET)) + return MHD_NO; + if (NULL == *con_cls) + { + *con_cls = connection; + return MHD_YES; + } + pass = NULL; + user = MHD_basic_auth_get_username_password (connection, &pass); + fail = ( (user == NULL) || + (0 != strcmp (user, "root")) || + (0 != strcmp (pass, "pa$$w0rd") ) ); + if (user != NULL) free (user); + if (pass != NULL) free (pass); + if (fail) + { + const char *page = "<html><body>Go away.</body></html>"; + response = + MHD_create_response_from_buffer (strlen (page), (void *) page, + MHD_RESPMEM_PERSISTENT); + ret = MHD_queue_basic_auth_fail_response (connection, + "my realm", + response); + } + else + { + const char *page = "<html><body>A secret.</body></html>"; + response = + MHD_create_response_from_buffer (strlen (page), (void *) page, + MHD_RESPMEM_PERSISTENT); + ret = MHD_queue_response (connection, MHD_HTTP_OK, response); + } + MHD_destroy_response (response); + return ret; +} + + See the `examples' directory for the complete example file. + +Remarks +======= + +For a proper server, the conditional statements leading to a return of +`MHD_NO' should yield a response with a more precise status code +instead of silently closing the connection. For example, failures of +memory allocation are best reported as _internal server error_ and +unexpected authentication methods as _400 bad request_. + +Exercises +========= + + * Make the server respond to wrong credentials (but otherwise + well-formed requests) with the recommended _401 unauthorized_ + status code. If the client still does not authenticate correctly + within the same connection, close it and store the client's IP + address for a certain time. (It is OK to check for expiration not + until the main thread wakes up again on the next connection.) If + the client fails authenticating three times during this period, + add it to another list for which the `AcceptPolicyCallback' + function denies connection (temporally). + + * With the network utility `netcat' connect and log the response of + a "GET" request as you did in the exercise of the first example, + this time to a file. Now stop the server and let _netcat_ listen + on the same port the server used to listen on and have it fake + being the proper server by giving the file's content as the + response (e.g. `cat log | nc -l -p 8888'). Pretending to think + your were connecting to the actual server, browse to the + eavesdropper and give the correct credentials. + + Copy and paste the encoded string you see in `netcat''s output to + some of the Base64 decode tools available online and see how both + the user's name and password could be completely restored. + + + +File: libmicrohttpd-tutorial.info, Node: Processing POST data, Next: Improved processing of POST data, Prev: Supporting basic authentication, Up: Top + +6 Processing POST data +********************** + +The previous chapters already have demonstrated a variety of +possibilities to send information to the HTTP server, but it is not +recommended that the _GET_ method is used to alter the way the server +operates. To induce changes on the server, the _POST_ method is +preferred over and is much more powerful than _GET_ and will be +introduced in this chapter. + + We are going to write an application that asks for the visitor's +name and, after the user has posted it, composes an individual response +text. Even though it was not mandatory to use the _POST_ method here, +as there is no permanent change caused by the POST, it is an +illustrative example on how to share data between different functions +for the same connection. Furthermore, the reader should be able to +extend it easily. + +GET request +=========== + +When the first _GET_ request arrives, the server shall respond with a +HTML page containing an edit field for the name. + +const char* askpage = "<html><body>\ + What's your name, Sir?<br>\ + <form action=\"/namepost\" method=\"post\">\ + <input name=\"name\" type=\"text\"\ + <input type=\"submit\" value=\" Send \"></form>\ + </body></html>"; + The `action' entry is the _URI_ to be called by the browser when +posting, and the `name' will be used later to be sure it is the +editbox's content that has been posted. + + We also prepare the answer page, where the name is to be filled in +later, and an error page as the response for anything but proper _GET_ +and _POST_ requests: + +const char* greatingpage="<html><body><h1>Welcome, %s!</center></h1></body></html>"; + +const char* errorpage="<html><body>This doesn't seem to be right.</body></html>"; + Whenever we need to send a page, we use an extra function `int +send_page(struct MHD_Connection *connection, const char* page)' for +this, which does not contain anything new and whose implementation is +therefore not discussed further in the tutorial. + +POST request +============ + +Posted data can be of arbitrary and considerable size; for example, if +a user uploads a big image to the server. Similar to the case of the +header fields, there may also be different streams of posted data, such +as one containing the text of an editbox and another the state of a +button. Likewise, we will have to register an iterator function that +is going to be called maybe several times not only if there are +different POSTs but also if one POST has only been received partly yet +and needs processing before another chunk can be received. + + Such an iterator function is called by a _postprocessor_, which must +be created upon arriving of the post request. We want the iterator +function to read the first post data which is tagged `name' and to +create an individual greeting string based on the template and the name. +But in order to pass this string to other functions and still be able +to differentiate different connections, we must first define a +structure to share the information, holding the most import entries. + +struct connection_info_struct +{ + int connectiontype; + char *answerstring; + struct MHD_PostProcessor *postprocessor; +}; + With these information available to the iterator function, it is able +to fulfill its task. Once it has composed the greeting string, it +returns `MHD_NO' to inform the post processor that it does not need to +be called again. Note that this function does not handle processing of +data for the same `key'. If we were to expect that the name will be +posted in several chunks, we had to expand the namestring dynamically +as additional parts of it with the same `key' came in. But in this +example, the name is assumed to fit entirely inside one single packet. + +static int +iterate_post (void *coninfo_cls, enum MHD_ValueKind kind, const char *key, + const char *filename, const char *content_type, + const char *transfer_encoding, const char *data, + uint64_t off, size_t size) +{ + struct connection_info_struct *con_info = coninfo_cls; + + if (0 == strcmp (key, "name")) + { + if ((size > 0) && (size <= MAXNAMESIZE)) + { + char *answerstring; + answerstring = malloc (MAXANSWERSIZE); + if (!answerstring) return MHD_NO; + + snprintf (answerstring, MAXANSWERSIZE, greatingpage, data); + con_info->answerstring = answerstring; + } + else con_info->answerstring = NULL; + + return MHD_NO; + } + + return MHD_YES; +} + Once a connection has been established, it can be terminated for many +reasons. As these reasons include unexpected events, we have to +register another function that cleans up any resources that might have +been allocated for that connection by us, namely the post processor and +the greetings string. This cleanup function must take into account that +it will also be called for finished requests other than _POST_ requests. + +void request_completed (void *cls, struct MHD_Connection *connection, + void **con_cls, + enum MHD_RequestTerminationCode toe) +{ + struct connection_info_struct *con_info = *con_cls; + + if (NULL == con_info) return; + if (con_info->connectiontype == POST) + { + MHD_destroy_post_processor (con_info->postprocessor); + if (con_info->answerstring) free (con_info->answerstring); + } + + free (con_info); + *con_cls = NULL; +} + _GNU libmicrohttpd_ is informed that it shall call the above function +when the daemon is started in the main function. + +... +daemon = MHD_start_daemon (MHD_USE_SELECT_INTERNALLY, PORT, NULL, NULL, + &answer_to_connection, NULL, + MHD_OPTION_NOTIFY_COMPLETED, &request_completed, NULL, + MHD_OPTION_END); +... + +Request handling +================ + +With all other functions prepared, we can now discuss the actual +request handling. + + On the first iteration for a new request, we start by allocating a +new instance of a `struct connection_info_struct' structure, which will +store all necessary information for later iterations and other +functions. + +static int +answer_to_connection (void *cls, struct MHD_Connection *connection, + const char *url, + const char *method, const char *version, + const char *upload_data, + size_t *upload_data_size, void **con_cls) +{ + if(NULL == *con_cls) + { + struct connection_info_struct *con_info; + + con_info = malloc (sizeof (struct connection_info_struct)); + if (NULL == con_info) return MHD_NO; + con_info->answerstring = NULL; + If the new request is a _POST_, the postprocessor must be created now. +In addition, the type of the request is stored for convenience. + if (0 == strcmp (method, "POST")) + { + con_info->postprocessor + = MHD_create_post_processor (connection, POSTBUFFERSIZE, + iterate_post, (void*) con_info); + + if (NULL == con_info->postprocessor) + { + free (con_info); + return MHD_NO; + } + con_info->connectiontype = POST; + } + else con_info->connectiontype = GET; + The address of our structure will both serve as the indicator for +successive iterations and to remember the particular details about the +connection. + *con_cls = (void*) con_info; + return MHD_YES; + } + The rest of the function will not be executed on the first iteration. +A _GET_ request is easily satisfied by sending the question form. + if (0 == strcmp (method, "GET")) + { + return send_page (connection, askpage); + } + In case of _POST_, we invoke the post processor for as long as data +keeps incoming, setting `*upload_data_size' to zero in order to +indicate that we have processed--or at least have considered--all of it. + if (0 == strcmp (method, "POST")) + { + struct connection_info_struct *con_info = *con_cls; + + if (*upload_data_size != 0) + { + MHD_post_process (con_info->postprocessor, upload_data, + *upload_data_size); + *upload_data_size = 0; + + return MHD_YES; + } + else if (NULL != con_info->answerstring) + return send_page (connection, con_info->answerstring); + } + Finally, if they are neither _GET_ nor _POST_ requests, the error page +is returned. + return send_page(connection, errorpage); +} + These were the important parts of the program `simplepost.c'. + + +File: libmicrohttpd-tutorial.info, Node: Improved processing of POST data, Next: Session management, Prev: Processing POST data, Up: Top + +7 Improved processing of POST data +********************************** + +The previous chapter introduced a way to upload data to the server, but +the developed example program has some shortcomings, such as not being +able to handle larger chunks of data. In this chapter, we are going to +discuss a more advanced server program that allows clients to upload a +file in order to have it stored on the server's filesystem. The server +shall also watch and limit the number of clients concurrently +uploading, responding with a proper busy message if necessary. + +Prepared answers +================ + +We choose to operate the server with the `SELECT_INTERNALLY' method. +This makes it easier to synchronize the global states at the cost of +possible delays for other connections if the processing of a request is +too slow. One of these variables that needs to be shared for all +connections is the total number of clients that are uploading. + +#define MAXCLIENTS 2 +static unsigned int nr_of_uploading_clients = 0; + If there are too many clients uploading, we want the server to respond +to all requests with a busy message. +const char* busypage = + "<html><body>This server is busy, please try again later.</body></html>"; + Otherwise, the server will send a _form_ that informs the user of the +current number of uploading clients, and ask her to pick a file on her +local filesystem which is to be uploaded. +const char* askpage = "<html><body>\n\ + Upload a file, please!<br>\n\ + There are %u clients uploading at the moment.<br>\n\ + <form action=\"/filepost\" method=\"post\" \ + enctype=\"multipart/form-data\">\n\ + <input name=\"file\" type=\"file\">\n\ + <input type=\"submit\" value=\" Send \"></form>\n\ + </body></html>"; + If the upload has succeeded, the server will respond with a message +saying so. +const char* completepage = "<html><body>The upload has been completed.</body></html>"; + We want the server to report internal errors, such as memory shortage +or file access problems, adequately. +const char* servererrorpage + = "<html><body>An internal server error has occured.</body></html>"; +const char* fileexistspage + = "<html><body>This file already exists.</body></html>"; + It would be tolerable to send all these responses undifferentiated +with a `200 HTTP_OK' status code but in order to improve the `HTTP' +conformance of our server a bit, we extend the `send_page' function so +that it accepts individual status codes. + +static int +send_page (struct MHD_Connection *connection, + const char* page, int status_code) +{ + int ret; + struct MHD_Response *response; + + response = MHD_create_response_from_buffer (strlen (page), (void*) page, + MHD_RESPMEM_MUST_COPY); + if (!response) return MHD_NO; + + ret = MHD_queue_response (connection, status_code, response); + MHD_destroy_response (response); + + return ret; +} + Note how we ask _MHD_ to make its own copy of the message data. The +reason behind this will become clear later. + +Connection cycle +================ + +The decision whether the server is busy or not is made right at the +beginning of the connection. To do that at this stage is especially +important for _POST_ requests because if no response is queued at this +point, and `MHD_YES' returned, _MHD_ will not sent any queued messages +until a postprocessor has been created and the post iterator is called +at least once. + +static int +answer_to_connection (void *cls, struct MHD_Connection *connection, + const char *url, + const char *method, const char *version, + const char *upload_data, + size_t *upload_data_size, void **con_cls) +{ + if (NULL == *con_cls) + { + struct connection_info_struct *con_info; + + if (nr_of_uploading_clients >= MAXCLIENTS) + return send_page(connection, busypage, MHD_HTTP_SERVICE_UNAVAILABLE); + If the server is not busy, the `connection_info' structure is +initialized as usual, with the addition of a filepointer for each +connection. + + con_info = malloc (sizeof (struct connection_info_struct)); + if (NULL == con_info) return MHD_NO; + con_info->fp = 0; + + if (0 == strcmp (method, "POST")) + { + ... + } + else con_info->connectiontype = GET; + + *con_cls = (void*) con_info; + + return MHD_YES; + } + For _POST_ requests, the postprocessor is created and we register a +new uploading client. From this point on, there are many possible +places for errors to occur that make it necessary to interrupt the +uploading process. We need a means of having the proper response +message ready at all times. Therefore, the `connection_info' structure +is extended to hold the most current response message so that whenever +a response is sent, the client will get the most informative message. +Here, the structure is initialized to "no error". + if (0 == strcmp (method, "POST")) + { + con_info->postprocessor + = MHD_create_post_processor (connection, POSTBUFFERSIZE, + iterate_post, (void*) con_info); + + if (NULL == con_info->postprocessor) + { + free (con_info); + return MHD_NO; + } + + nr_of_uploading_clients++; + + con_info->connectiontype = POST; + con_info->answercode = MHD_HTTP_OK; + con_info->answerstring = completepage; + } + else con_info->connectiontype = GET; + If the connection handler is called for the second time, _GET_ +requests will be answered with the _form_. We can keep the buffer under +function scope, because we asked _MHD_ to make its own copy of it for +as long as it is needed. + if (0 == strcmp (method, "GET")) + { + int ret; + char buffer[1024]; + + sprintf (buffer, askpage, nr_of_uploading_clients); + return send_page (connection, buffer, MHD_HTTP_OK); + } + The rest of the `answer_to_connection' function is very similar to the +`simplepost.c' example, except the more flexible content of the +responses. The _POST_ data is processed until there is none left and +the execution falls through to return an error page if the connection +constituted no expected request method. + if (0 == strcmp (method, "POST")) + { + struct connection_info_struct *con_info = *con_cls; + + if (0 != *upload_data_size) + { + MHD_post_process (con_info->postprocessor, + upload_data, *upload_data_size); + *upload_data_size = 0; + + return MHD_YES; + } + else + return send_page (connection, con_info->answerstring, + con_info->answercode); + } + + return send_page(connection, errorpage, MHD_HTTP_BAD_REQUEST); +} + +Storing to data +=============== + +Unlike the `simplepost.c' example, here it is to be expected that post +iterator will be called several times now. This means that for any +given connection (there might be several concurrent of them) the posted +data has to be written to the correct file. That is why we store a file +handle in every `connection_info', so that the it is preserved between +successive iterations. +static int +iterate_post (void *coninfo_cls, enum MHD_ValueKind kind, + const char *key, + const char *filename, const char *content_type, + const char *transfer_encoding, const char *data, + uint64_t off, size_t size) +{ + struct connection_info_struct *con_info = coninfo_cls; + Because the following actions depend heavily on correct file +processing, which might be error prone, we default to reporting +internal errors in case anything will go wrong. + +con_info->answerstring = servererrorpage; +con_info->answercode = MHD_HTTP_INTERNAL_SERVER_ERROR; + In the "askpage" _form_, we told the client to label its post data +with the "file" key. Anything else would be an error. + + if (0 != strcmp (key, "file")) return MHD_NO; + If the iterator is called for the first time, no file will have been +opened yet. The `filename' string contains the name of the file +(without any paths) the user selected on his system. We want to take +this as the name the file will be stored on the server and make sure no +file of that name exists (or is being uploaded) before we create one +(note that the code below technically contains a race between the two +"fopen" calls, but we will overlook this for portability sake). + if (!con_info->fp) + { + if (NULL != (fp = fopen (filename, "rb")) ) + { + fclose (fp); + con_info->answerstring = fileexistspage; + con_info->answercode = MHD_HTTP_FORBIDDEN; + return MHD_NO; + } + + con_info->fp = fopen (filename, "ab"); + if (!con_info->fp) return MHD_NO; + } + Occasionally, the iterator function will be called even when there are +0 new bytes to process. The server only needs to write data to the file +if there is some. +if (size > 0) + { + if (!fwrite (data, size, sizeof(char), con_info->fp)) + return MHD_NO; + } + If this point has been reached, everything worked well for this +iteration and the response can be set to success again. If the upload +has finished, this iterator function will not be called again. + con_info->answerstring = completepage; + con_info->answercode = MHD_HTTP_OK; + + return MHD_YES; +} + The new client was registered when the postprocessor was created. +Likewise, we unregister the client on destroying the postprocessor when +the request is completed. +void request_completed (void *cls, struct MHD_Connection *connection, + void **con_cls, + enum MHD_RequestTerminationCode toe) +{ + struct connection_info_struct *con_info = *con_cls; + + if (NULL == con_info) return; + + if (con_info->connectiontype == POST) + { + if (NULL != con_info->postprocessor) + { + MHD_destroy_post_processor (con_info->postprocessor); + nr_of_uploading_clients--; + } + + if (con_info->fp) fclose (con_info->fp); + } + + free (con_info); + *con_cls = NULL; +} + This is essentially the whole example `largepost.c'. + +Remarks +======= + +Now that the clients are able to create files on the server, security +aspects are becoming even more important than before. Aside from proper +client authentication, the server should always make sure explicitly +that no files will be created outside of a dedicated upload directory. +In particular, filenames must be checked to not contain strings like +"../". + + +File: libmicrohttpd-tutorial.info, Node: Session management, Next: Adding a layer of security, Prev: Improved processing of POST data, Up: Top + +8 Session management +******************** + +This chapter discusses how one should manage sessions, that is, share +state between multiple HTTP requests from the same user. We use a +simple example where the user submits multiple forms and the server is +supposed to accumulate state from all of these forms. Naturally, as +this is a network protocol, our session mechanism must support having +many users with many concurrent sessions at the same time. + + In order to track users, we use a simple session cookie. A session +cookie expires when the user closes the browser. Changing from session +cookies to persistent cookies only requires adding an expiration time +to the cookie. The server creates a fresh session cookie whenever a +request without a cookie is received, or if the supplied session cookie +is not known to the server. + +Looking up the cookie +===================== + +Since MHD parses the HTTP cookie header for us, looking up an existing +cookie is straightforward: + +FIXME. + + Here, FIXME is the name we chose for our session cookie. + +Setting the cookie header +========================= + +MHD requires the user to provide the full cookie format string in order +to set cookies. In order to generate a unique cookie, our example +creates a random 64-character text string to be used as the value of +the cookie: + +FIXME. + + Given this cookie value, we can then set the cookie header in our +HTTP response as follows: + +FIXME. + +Remark: Session expiration +========================== + +It is of course possible that clients stop their interaction with the +server at any time. In order to avoid using too much storage, the +server must thus discard inactive sessions at some point. Our example +implements this by discarding inactive sessions after a certain amount +of time. Alternatively, the implementation may limit the total number +of active sessions. Which bounds are used for idle sessions or the +total number of sessions obviously depends largely on the type of the +application and available server resources. + +Example code +============ + +A sample application implementing a website with multiple forms (which +are dynamically created using values from previous POST requests from +the same session) is available as the example `sessions.c'. + + Note that the example uses a simple, $O(n)$ linked list traversal to +look up sessions and to expire old sessions. Using a hash table and a +heap would be more appropriate if a large number of concurrent sessions +is expected. + +Remarks +======= + +Naturally, it is quite conceivable to store session data in a database +instead of in memory. Still, having mechanisms to expire data +associated with long-time idle sessions (where the business process has +still not finished) is likely a good idea. + + +File: libmicrohttpd-tutorial.info, Node: Adding a layer of security, Next: Bibliography, Prev: Session management, Up: Top + +9 Adding a layer of security +**************************** + +We left the basic authentication chapter with the unsatisfactory +conclusion that any traffic, including the credentials, could be +intercepted by anyone between the browser client and the server. +Protecting the data while it is sent over unsecured lines will be the +goal of this chapter. + + Since version 0.4, the _MHD_ library includes support for encrypting +the traffic by employing SSL/TSL. If _GNU libmicrohttpd_ has been +configured to support these, encryption and decryption can be applied +transparently on the data being sent, with only minimal changes to the +actual source code of the example. + +Preparation +=========== + +First, a private key for the server will be generated. With this key, +the server will later be able to authenticate itself to the +client--preventing anyone else from stealing the password by faking its +identity. The _OpenSSL_ suite, which is available on many operating +systems, can generate such a key. For the scope of this tutorial, we +will be content with a 1024 bit key: +> openssl genrsa -out server.key 1024 + In addition to the key, a certificate describing the server in human +readable tokens is also needed. This certificate will be attested with +our aforementioned key. In this way, we obtain a self-signed +certificate, valid for one year. + +> openssl req -days 365 -out server.pem -new -x509 -key server.key + To avoid unnecessary error messages in the browser, the certificate +needs to have a name that matches the _URI_, for example, "localhost" +or the domain. If you plan to have a publicly reachable server, you +will need to ask a trusted third party, called _Certificate Authority_, +or _CA_, to attest the certificate for you. This way, any visitor can +make sure the server's identity is real. + + Whether the server's certificate is signed by us or a third party, +once it has been accepted by the client, both sides will be +communicating over encrypted channels. From this point on, it is the +client's turn to authenticate itself. But this has already been +implemented in the basic authentication scheme. + +Changing the source code +======================== + +We merely have to extend the server program so that it loads the two +files into memory, + +int +main () +{ + struct MHD_Daemon *daemon; + char *key_pem; + char *cert_pem; + + key_pem = load_file (SERVERKEYFILE); + cert_pem = load_file (SERVERCERTFILE); + + if ((key_pem == NULL) || (cert_pem == NULL)) + { + printf ("The key/certificate files could not be read.\n"); + return 1; + } + and then we point the _MHD_ daemon to it upon initalization. + + daemon = MHD_start_daemon (MHD_USE_SELECT_INTERNALLY | MHD_USE_SSL, + PORT, NULL, NULL, + &answer_to_connection, NULL, + MHD_OPTION_HTTPS_MEM_KEY, key_pem, + MHD_OPTION_HTTPS_MEM_CERT, cert_pem, + MHD_OPTION_END); + + if (NULL == daemon) + { + printf ("%s\n", cert_pem); + + free (key_pem); + free (cert_pem); + + return 1; + } + The rest consists of little new besides some additional memory +cleanups. + + getchar (); + + MHD_stop_daemon (daemon); + free (key_pem); + free (cert_pem); + + return 0; +} + The rather unexciting file loader can be found in the complete example +`tlsauthentication.c'. + +Remarks +======= + + * While the standard _HTTP_ port is 80, it is 443 for _HTTPS_. The + common internet browsers assume standard _HTTP_ if they are asked + to access other ports than these. Therefore, you will have to type + `https://localhost:8888' explicitly when you test the example, or + the browser will not know how to handle the answer properly. + + * The remaining weak point is the question how the server will be + trusted initially. Either a _CA_ signs the certificate or the + client obtains the key over secure means. Anyway, the clients have + to be aware (or configured) that they should not accept + certificates of unknown origin. + + * The introduced method of certificates makes it mandatory to set an + expiration date--making it less feasible to hardcode certificates + in embedded devices. + + * The cryptographic facilities consume memory space and computing + time. For this reason, websites usually consists both of + uncritically _HTTP_ parts and secured _HTTPS_. + + +Client authentication +===================== + +You can also use MHD to authenticate the client via SSL/TLS certificates +(as an alternative to using the password-based Basic or Digest +authentication). To do this, you will need to link your application +against _gnutls_. Next, when you start the MHD daemon, you must +specify the root CA that you're willing to trust: + daemon = MHD_start_daemon (MHD_USE_SELECT_INTERNALLY | MHD_USE_SSL, + PORT, NULL, NULL, + &answer_to_connection, NULL, + MHD_OPTION_HTTPS_MEM_KEY, key_pem, + MHD_OPTION_HTTPS_MEM_CERT, cert_pem, + MHD_OPTION_HTTPS_MEM_TRUST, root_ca_pem, + MHD_OPTION_END); + + With this, you can then obtain client certificates for each session. +In order to obtain the identity of the client, you first need to obtain +the raw GnuTLS session handle from _MHD_ using +`MHD_get_connection_info'. + +#include <gnutls/gnutls.h> +#include <gnutls/x509.h> + +gnutls_session_t tls_session; +union MHD_ConnectionInfo *ci; + +ci = MHD_get_connection_info (connection, + MHD_CONNECTION_INFO_GNUTLS_SESSION); +tls_session = ci->tls_session; + + You can then extract the client certificate: + +/** + * Get the client's certificate + * + * @param tls_session the TLS session + * @return NULL if no valid client certificate could be found, a pointer + * to the certificate if found + */ +static gnutls_x509_crt_t +get_client_certificate (gnutls_session_t tls_session) +{ + unsigned int listsize; + const gnutls_datum_t * pcert; + gnutls_certificate_status_t client_cert_status; + gnutls_x509_crt_t client_cert; + + if (tls_session == NULL) + return NULL; + if (gnutls_certificate_verify_peers2(tls_session, + &client_cert_status)) + return NULL; + pcert = gnutls_certificate_get_peers(tls_session, + &listsize); + if ( (pcert == NULL) || + (listsize == 0)) + { + fprintf (stderr, + "Failed to retrieve client certificate chain\n"); + return NULL; + } + if (gnutls_x509_crt_init(&client_cert)) + { + fprintf (stderr, + "Failed to initialize client certificate\n"); + return NULL; + } + /* Note that by passing values between 0 and listsize here, you + can get access to the CA's certs */ + if (gnutls_x509_crt_import(client_cert, + &pcert[0], + GNUTLS_X509_FMT_DER)) + { + fprintf (stderr, + "Failed to import client certificate\n"); + gnutls_x509_crt_deinit(client_cert); + return NULL; + } + return client_cert; +} + + Using the client certificate, you can then get the client's +distinguished name and alternative names: + +/** + * Get the distinguished name from the client's certificate + * + * @param client_cert the client certificate + * @return NULL if no dn or certificate could be found, a pointer + * to the dn if found + */ +char * +cert_auth_get_dn(gnutls_x509_crt_c client_cert) +{ + char* buf; + size_t lbuf; + + lbuf = 0; + gnutls_x509_crt_get_dn(client_cert, NULL, &lbuf); + buf = malloc(lbuf); + if (buf == NULL) + { + fprintf (stderr, + "Failed to allocate memory for certificate dn\n"); + return NULL; + } + gnutls_x509_crt_get_dn(client_cert, buf, &lbuf); + return buf; +} + + +/** + * Get the alternative name of specified type from the client's certificate + * + * @param client_cert the client certificate + * @param nametype The requested name type + * @param index The position of the alternative name if multiple names are + * matching the requested type, 0 for the first matching name + * @return NULL if no matching alternative name could be found, a pointer + * to the alternative name if found + */ +char * +MHD_cert_auth_get_alt_name(gnutls_x509_crt_t client_cert, + int nametype, + unsigned int index) +{ + char* buf; + size_t lbuf; + unsigned int seq; + unsigned int subseq; + unsigned int type; + int result; + + subseq = 0; + for (seq=0;;seq++) + { + lbuf = 0; + result = gnutls_x509_crt_get_subject_alt_name2(client_cert, seq, NULL, &lbuf, + &type, NULL); + if (result == GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE) + return NULL; + if (nametype != (int) type) + continue; + if (subseq == index) + break; + subseq++; + } + buf = malloc(lbuf); + if (buf == NULL) + { + fprintf (stderr, + "Failed to allocate memory for certificate alt name\n"); + return NULL; + } + result = gnutls_x509_crt_get_subject_alt_name2(client_cert, + seq, + buf, + &lbuf, + NULL, NULL); + if (result != nametype) + { + fprintf (stderr, + "Unexpected return value from gnutls: %d\n", + result); + free (buf); + return NULL; + } + return buf; +} + + Finally, you should release the memory associated with the client +certificate: + +gnutls_x509_crt_deinit (client_cert); + +Using TLS Server Name Indication (SNI) +====================================== + +SNI enables hosting multiple domains under one IP address with TLS. So +SNI is the TLS-equivalent of virtual hosting. To use SNI with MHD, you +need at least GnuTLS 3.0. The main change compared to the simple +hosting of one domain is that you need to provide a callback instead of +the key and certificate. For example, when you start the MHD daemon, +you could do this: + daemon = MHD_start_daemon (MHD_USE_SELECT_INTERNALLY | MHD_USE_SSL, + PORT, NULL, NULL, + &answer_to_connection, NULL, + MHD_OPTION_HTTPS_CERT_CALLBACK, &sni_callback, + MHD_OPTION_END); + Here, `sni_callback' is the name of a function that you will have to +implement to retrieve the X.509 certificate for an incoming connection. +The callback has type `gnutls_certificate_retrieve_function2' and is +documented in the GnuTLS API for the +`gnutls_certificate_set_retrieve_function2' as follows: + + -- Function Pointer: int *gnutls_certificate_retrieve_function2 + (gnutls_session_t, const gnutls_datum_t* req_ca_dn, int + nreqs, const gnutls_pk_algorithm_t* pk_algos, int + pk_algos_length, gnutls_pcert_st** pcert, unsigned int + *pcert_length, gnutls_privkey_t * pkey) + REQ_CA_CERT + is only used in X.509 certificates. Contains a list with the + CA names that the server considers trusted. Normally we + should send a certificate that is signed by one of these CAs. + These names are DER encoded. To get a more meaningful value + use the function `gnutls_x509_rdn_get()'. + + PK_ALGOS + contains a list with server’s acceptable signature + algorithms. The certificate returned should support the + server’s given algorithms. + + PCERT + should contain a single certificate and public or a list of + them. + + PCERT_LENGTH + is the size of the previous list. + + PKEY + is the private key. + + A possible implementation of this callback would look like this: + +struct Hosts +{ + struct Hosts *next; + const char *hostname; + gnutls_pcert_st pcrt; + gnutls_privkey_t key; +}; + +static struct Hosts *hosts; + +int +sni_callback (gnutls_session_t session, + const gnutls_datum_t* req_ca_dn, + int nreqs, + const gnutls_pk_algorithm_t* pk_algos, + int pk_algos_length, + gnutls_pcert_st** pcert, + unsigned int *pcert_length, + gnutls_privkey_t * pkey) +{ + char name[256]; + size_t name_len; + struct Hosts *host; + unsigned int type; + + name_len = sizeof (name); + if (GNUTLS_E_SUCCESS != + gnutls_server_name_get (session, + name, + &name_len, + &type, + 0 /* index */)) + return -1; + for (host = hosts; NULL != host; host = host->next) + if (0 == strncmp (name, host->hostname, name_len)) + break; + if (NULL == host) + { + fprintf (stderr, + "Need certificate for %.*s\n", + (int) name_len, + name); + return -1; + } + fprintf (stderr, + "Returning certificate for %.*s\n", + (int) name_len, + name); + *pkey = host->key; + *pcert_length = 1; + *pcert = &host->pcrt; + return 0; +} + + Note that MHD cannot offer passing a closure or any other additional +information to this callback, as the GnuTLS API unfortunately does not +permit this at this point. + + The `hosts' list can be initialized by loading the private keys and +X.509 certificats from disk as follows: + +static void +load_keys(const char *hostname, + const char *CERT_FILE, + const char *KEY_FILE) +{ + int ret; + gnutls_datum_t data; + struct Hosts *host; + + host = malloc (sizeof (struct Hosts)); + host->hostname = hostname; + host->next = hosts; + hosts = host; + + ret = gnutls_load_file (CERT_FILE, &data); + if (ret < 0) + { + fprintf (stderr, + "*** Error loading certificate file %s.\n", + CERT_FILE); + exit(1); + } + ret = + gnutls_pcert_import_x509_raw (&host->pcrt, &data, GNUTLS_X509_FMT_PEM, + 0); + if (ret < 0) + { + fprintf(stderr, + "*** Error loading certificate file: %s\n", + gnutls_strerror (ret)); + exit(1); + } + gnutls_free (data.data); + + ret = gnutls_load_file (KEY_FILE, &data); + if (ret < 0) + { + fprintf (stderr, + "*** Error loading key file %s.\n", + KEY_FILE); + exit(1); + } + + gnutls_privkey_init (&host->key); + ret = + gnutls_privkey_import_x509_raw (host->key, + &data, GNUTLS_X509_FMT_PEM, + NULL, 0); + if (ret < 0) + { + fprintf (stderr, + "*** Error loading key file: %s\n", + gnutls_strerror (ret)); + exit(1); + } + gnutls_free (data.data); +} + + The code above was largely lifted from GnuTLS. You can find other +methods for initializing certificates and keys in the GnuTLS manual and +source code. + + +File: libmicrohttpd-tutorial.info, Node: Bibliography, Next: License text, Prev: Adding a layer of security, Up: Top + +Appendix A Bibliography +*********************** + +API reference +============= + + * The _GNU libmicrohttpd_ manual by Marco Maggi and Christian + Grothoff 2008 `http://gnunet.org/libmicrohttpd/microhttpd.html' + + * All referenced RFCs can be found on the website of _The Internet + Engineering Task Force_ `http://www.ietf.org/' + + * _RFC 2616_: Fielding, R., Gettys, J., Mogul, J., Frystyk, H., and + T. Berners-Lee, "Hypertext Transfer Protocol - HTTP/1.1", RFC + 2016, January 1997. + + * _RFC 2617_: Franks, J., Hallam-Baker, P., Hostetler, J., Lawrence, + S., Leach, P., Luotonen, A., and L. Stewart, "HTTP Authentication: + Basic and Digest Access Authentication", RFC 2617, June 1999. + + * A well-structured _HTML_ reference can be found on + `http://www.echoecho.com/html.htm' + + For those readers understanding German or French, there is an + excellent document both for learning _HTML_ and for reference, + whose English version unfortunately has been discontinued. + `http://de.selfhtml.org/' and `http://fr.selfhtml.org/' + + + +File: libmicrohttpd-tutorial.info, Node: License text, Next: Example programs, Prev: Bibliography, Up: Top + +Appendix B GNU Free Documentation License +***************************************** + + Version 1.3, 3 November 2008 + + Copyright (C) 2000, 2001, 2002, 2007, 2008 Free Software Foundation, Inc. + `http://fsf.org/' + + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + 0. PREAMBLE + + The purpose of this License is to make a manual, textbook, or other + functional and useful document "free" in the sense of freedom: to + assure everyone the effective freedom to copy and redistribute it, + with or without modifying it, either commercially or + noncommercially. Secondarily, this License preserves for the + author and publisher a way to get credit for their work, while not + being considered responsible for modifications made by others. + + This License is a kind of "copyleft", which means that derivative + works of the document must themselves be free in the same sense. + It complements the GNU General Public License, which is a copyleft + license designed for free software. + + We have designed this License in order to use it for manuals for + free software, because free software needs free documentation: a + free program should come with manuals providing the same freedoms + that the software does. But this License is not limited to + software manuals; it can be used for any textual work, regardless + of subject matter or whether it is published as a printed book. + We recommend this License principally for works whose purpose is + instruction or reference. + + 1. APPLICABILITY AND DEFINITIONS + + This License applies to any manual or other work, in any medium, + that contains a notice placed by the copyright holder saying it + can be distributed under the terms of this License. Such a notice + grants a world-wide, royalty-free license, unlimited in duration, + to use that work under the conditions stated herein. The + "Document", below, refers to any such manual or work. Any member + of the public is a licensee, and is addressed as "you". You + accept the license if you copy, modify or distribute the work in a + way requiring permission under copyright law. + + A "Modified Version" of the Document means any work containing the + Document or a portion of it, either copied verbatim, or with + modifications and/or translated into another language. + + A "Secondary Section" is a named appendix or a front-matter section + of the Document that deals exclusively with the relationship of the + publishers or authors of the Document to the Document's overall + subject (or to related matters) and contains nothing that could + fall directly within that overall subject. (Thus, if the Document + is in part a textbook of mathematics, a Secondary Section may not + explain any mathematics.) The relationship could be a matter of + historical connection with the subject or with related matters, or + of legal, commercial, philosophical, ethical or political position + regarding them. + + The "Invariant Sections" are certain Secondary Sections whose + titles are designated, as being those of Invariant Sections, in + the notice that says that the Document is released under this + License. If a section does not fit the above definition of + Secondary then it is not allowed to be designated as Invariant. + The Document may contain zero Invariant Sections. If the Document + does not identify any Invariant Sections then there are none. + + The "Cover Texts" are certain short passages of text that are + listed, as Front-Cover Texts or Back-Cover Texts, in the notice + that says that the Document is released under this License. A + Front-Cover Text may be at most 5 words, and a Back-Cover Text may + be at most 25 words. + + A "Transparent" copy of the Document means a machine-readable copy, + represented in a format whose specification is available to the + general public, that is suitable for revising the document + straightforwardly with generic text editors or (for images + composed of pixels) generic paint programs or (for drawings) some + widely available drawing editor, and that is suitable for input to + text formatters or for automatic translation to a variety of + formats suitable for input to text formatters. A copy made in an + otherwise Transparent file format whose markup, or absence of + markup, has been arranged to thwart or discourage subsequent + modification by readers is not Transparent. An image format is + not Transparent if used for any substantial amount of text. A + copy that is not "Transparent" is called "Opaque". + + Examples of suitable formats for Transparent copies include plain + ASCII without markup, Texinfo input format, LaTeX input format, + SGML or XML using a publicly available DTD, and + standard-conforming simple HTML, PostScript or PDF designed for + human modification. Examples of transparent image formats include + PNG, XCF and JPG. Opaque formats include proprietary formats that + can be read and edited only by proprietary word processors, SGML or + XML for which the DTD and/or processing tools are not generally + available, and the machine-generated HTML, PostScript or PDF + produced by some word processors for output purposes only. + + The "Title Page" means, for a printed book, the title page itself, + plus such following pages as are needed to hold, legibly, the + material this License requires to appear in the title page. For + works in formats which do not have any title page as such, "Title + Page" means the text near the most prominent appearance of the + work's title, preceding the beginning of the body of the text. + + The "publisher" means any person or entity that distributes copies + of the Document to the public. + + A section "Entitled XYZ" means a named subunit of the Document + whose title either is precisely XYZ or contains XYZ in parentheses + following text that translates XYZ in another language. (Here XYZ + stands for a specific section name mentioned below, such as + "Acknowledgements", "Dedications", "Endorsements", or "History".) + To "Preserve the Title" of such a section when you modify the + Document means that it remains a section "Entitled XYZ" according + to this definition. + + The Document may include Warranty Disclaimers next to the notice + which states that this License applies to the Document. These + Warranty Disclaimers are considered to be included by reference in + this License, but only as regards disclaiming warranties: any other + implication that these Warranty Disclaimers may have is void and + has no effect on the meaning of this License. + + 2. VERBATIM COPYING + + You may copy and distribute the Document in any medium, either + commercially or noncommercially, provided that this License, the + copyright notices, and the license notice saying this License + applies to the Document are reproduced in all copies, and that you + add no other conditions whatsoever to those of this License. You + may not use technical measures to obstruct or control the reading + or further copying of the copies you make or distribute. However, + you may accept compensation in exchange for copies. If you + distribute a large enough number of copies you must also follow + the conditions in section 3. + + You may also lend copies, under the same conditions stated above, + and you may publicly display copies. + + 3. COPYING IN QUANTITY + + If you publish printed copies (or copies in media that commonly + have printed covers) of the Document, numbering more than 100, and + the Document's license notice requires Cover Texts, you must + enclose the copies in covers that carry, clearly and legibly, all + these Cover Texts: Front-Cover Texts on the front cover, and + Back-Cover Texts on the back cover. Both covers must also clearly + and legibly identify you as the publisher of these copies. The + front cover must present the full title with all words of the + title equally prominent and visible. You may add other material + on the covers in addition. Copying with changes limited to the + covers, as long as they preserve the title of the Document and + satisfy these conditions, can be treated as verbatim copying in + other respects. + + If the required texts for either cover are too voluminous to fit + legibly, you should put the first ones listed (as many as fit + reasonably) on the actual cover, and continue the rest onto + adjacent pages. + + If you publish or distribute Opaque copies of the Document + numbering more than 100, you must either include a + machine-readable Transparent copy along with each Opaque copy, or + state in or with each Opaque copy a computer-network location from + which the general network-using public has access to download + using public-standard network protocols a complete Transparent + copy of the Document, free of added material. If you use the + latter option, you must take reasonably prudent steps, when you + begin distribution of Opaque copies in quantity, to ensure that + this Transparent copy will remain thus accessible at the stated + location until at least one year after the last time you + distribute an Opaque copy (directly or through your agents or + retailers) of that edition to the public. + + It is requested, but not required, that you contact the authors of + the Document well before redistributing any large number of + copies, to give them a chance to provide you with an updated + version of the Document. + + 4. MODIFICATIONS + + You may copy and distribute a Modified Version of the Document + under the conditions of sections 2 and 3 above, provided that you + release the Modified Version under precisely this License, with + the Modified Version filling the role of the Document, thus + licensing distribution and modification of the Modified Version to + whoever possesses a copy of it. In addition, you must do these + things in the Modified Version: + + A. Use in the Title Page (and on the covers, if any) a title + distinct from that of the Document, and from those of + previous versions (which should, if there were any, be listed + in the History section of the Document). You may use the + same title as a previous version if the original publisher of + that version gives permission. + + B. List on the Title Page, as authors, one or more persons or + entities responsible for authorship of the modifications in + the Modified Version, together with at least five of the + principal authors of the Document (all of its principal + authors, if it has fewer than five), unless they release you + from this requirement. + + C. State on the Title page the name of the publisher of the + Modified Version, as the publisher. + + D. Preserve all the copyright notices of the Document. + + E. Add an appropriate copyright notice for your modifications + adjacent to the other copyright notices. + + F. Include, immediately after the copyright notices, a license + notice giving the public permission to use the Modified + Version under the terms of this License, in the form shown in + the Addendum below. + + G. Preserve in that license notice the full lists of Invariant + Sections and required Cover Texts given in the Document's + license notice. + + H. Include an unaltered copy of this License. + + I. Preserve the section Entitled "History", Preserve its Title, + and add to it an item stating at least the title, year, new + authors, and publisher of the Modified Version as given on + the Title Page. If there is no section Entitled "History" in + the Document, create one stating the title, year, authors, + and publisher of the Document as given on its Title Page, + then add an item describing the Modified Version as stated in + the previous sentence. + + J. Preserve the network location, if any, given in the Document + for public access to a Transparent copy of the Document, and + likewise the network locations given in the Document for + previous versions it was based on. These may be placed in + the "History" section. You may omit a network location for a + work that was published at least four years before the + Document itself, or if the original publisher of the version + it refers to gives permission. + + K. For any section Entitled "Acknowledgements" or "Dedications", + Preserve the Title of the section, and preserve in the + section all the substance and tone of each of the contributor + acknowledgements and/or dedications given therein. + + L. Preserve all the Invariant Sections of the Document, + unaltered in their text and in their titles. Section numbers + or the equivalent are not considered part of the section + titles. + + M. Delete any section Entitled "Endorsements". Such a section + may not be included in the Modified Version. + + N. Do not retitle any existing section to be Entitled + "Endorsements" or to conflict in title with any Invariant + Section. + + O. Preserve any Warranty Disclaimers. + + If the Modified Version includes new front-matter sections or + appendices that qualify as Secondary Sections and contain no + material copied from the Document, you may at your option + designate some or all of these sections as invariant. To do this, + add their titles to the list of Invariant Sections in the Modified + Version's license notice. These titles must be distinct from any + other section titles. + + You may add a section Entitled "Endorsements", provided it contains + nothing but endorsements of your Modified Version by various + parties--for example, statements of peer review or that the text + has been approved by an organization as the authoritative + definition of a standard. + + You may add a passage of up to five words as a Front-Cover Text, + and a passage of up to 25 words as a Back-Cover Text, to the end + of the list of Cover Texts in the Modified Version. Only one + passage of Front-Cover Text and one of Back-Cover Text may be + added by (or through arrangements made by) any one entity. If the + Document already includes a cover text for the same cover, + previously added by you or by arrangement made by the same entity + you are acting on behalf of, you may not add another; but you may + replace the old one, on explicit permission from the previous + publisher that added the old one. + + The author(s) and publisher(s) of the Document do not by this + License give permission to use their names for publicity for or to + assert or imply endorsement of any Modified Version. + + 5. COMBINING DOCUMENTS + + You may combine the Document with other documents released under + this License, under the terms defined in section 4 above for + modified versions, provided that you include in the combination + all of the Invariant Sections of all of the original documents, + unmodified, and list them all as Invariant Sections of your + combined work in its license notice, and that you preserve all + their Warranty Disclaimers. + + The combined work need only contain one copy of this License, and + multiple identical Invariant Sections may be replaced with a single + copy. If there are multiple Invariant Sections with the same name + but different contents, make the title of each such section unique + by adding at the end of it, in parentheses, the name of the + original author or publisher of that section if known, or else a + unique number. Make the same adjustment to the section titles in + the list of Invariant Sections in the license notice of the + combined work. + + In the combination, you must combine any sections Entitled + "History" in the various original documents, forming one section + Entitled "History"; likewise combine any sections Entitled + "Acknowledgements", and any sections Entitled "Dedications". You + must delete all sections Entitled "Endorsements." + + 6. COLLECTIONS OF DOCUMENTS + + You may make a collection consisting of the Document and other + documents released under this License, and replace the individual + copies of this License in the various documents with a single copy + that is included in the collection, provided that you follow the + rules of this License for verbatim copying of each of the + documents in all other respects. + + You may extract a single document from such a collection, and + distribute it individually under this License, provided you insert + a copy of this License into the extracted document, and follow + this License in all other respects regarding verbatim copying of + that document. + + 7. AGGREGATION WITH INDEPENDENT WORKS + + A compilation of the Document or its derivatives with other + separate and independent documents or works, in or on a volume of + a storage or distribution medium, is called an "aggregate" if the + copyright resulting from the compilation is not used to limit the + legal rights of the compilation's users beyond what the individual + works permit. When the Document is included in an aggregate, this + License does not apply to the other works in the aggregate which + are not themselves derivative works of the Document. + + If the Cover Text requirement of section 3 is applicable to these + copies of the Document, then if the Document is less than one half + of the entire aggregate, the Document's Cover Texts may be placed + on covers that bracket the Document within the aggregate, or the + electronic equivalent of covers if the Document is in electronic + form. Otherwise they must appear on printed covers that bracket + the whole aggregate. + + 8. TRANSLATION + + Translation is considered a kind of modification, so you may + distribute translations of the Document under the terms of section + 4. Replacing Invariant Sections with translations requires special + permission from their copyright holders, but you may include + translations of some or all Invariant Sections in addition to the + original versions of these Invariant Sections. You may include a + translation of this License, and all the license notices in the + Document, and any Warranty Disclaimers, provided that you also + include the original English version of this License and the + original versions of those notices and disclaimers. In case of a + disagreement between the translation and the original version of + this License or a notice or disclaimer, the original version will + prevail. + + If a section in the Document is Entitled "Acknowledgements", + "Dedications", or "History", the requirement (section 4) to + Preserve its Title (section 1) will typically require changing the + actual title. + + 9. TERMINATION + + You may not copy, modify, sublicense, or distribute the Document + except as expressly provided under this License. Any attempt + otherwise to copy, modify, sublicense, or distribute it is void, + and will automatically terminate your rights under this License. + + However, if you cease all violation of this License, then your + license from a particular copyright holder is reinstated (a) + provisionally, unless and until the copyright holder explicitly + and finally terminates your license, and (b) permanently, if the + copyright holder fails to notify you of the violation by some + reasonable means prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is + reinstated permanently if the copyright holder notifies you of the + violation by some reasonable means, this is the first time you have + received notice of violation of this License (for any work) from + that copyright holder, and you cure the violation prior to 30 days + after your receipt of the notice. + + Termination of your rights under this section does not terminate + the licenses of parties who have received copies or rights from + you under this License. If your rights have been terminated and + not permanently reinstated, receipt of a copy of some or all of + the same material does not give you any rights to use it. + + 10. FUTURE REVISIONS OF THIS LICENSE + + The Free Software Foundation may publish new, revised versions of + the GNU Free Documentation License from time to time. Such new + versions will be similar in spirit to the present version, but may + differ in detail to address new problems or concerns. See + `http://www.gnu.org/copyleft/'. + + Each version of the License is given a distinguishing version + number. If the Document specifies that a particular numbered + version of this License "or any later version" applies to it, you + have the option of following the terms and conditions either of + that specified version or of any later version that has been + published (not as a draft) by the Free Software Foundation. If + the Document does not specify a version number of this License, + you may choose any version ever published (not as a draft) by the + Free Software Foundation. If the Document specifies that a proxy + can decide which future versions of this License can be used, that + proxy's public statement of acceptance of a version permanently + authorizes you to choose that version for the Document. + + 11. RELICENSING + + "Massive Multiauthor Collaboration Site" (or "MMC Site") means any + World Wide Web server that publishes copyrightable works and also + provides prominent facilities for anybody to edit those works. A + public wiki that anybody can edit is an example of such a server. + A "Massive Multiauthor Collaboration" (or "MMC") contained in the + site means any set of copyrightable works thus published on the MMC + site. + + "CC-BY-SA" means the Creative Commons Attribution-Share Alike 3.0 + license published by Creative Commons Corporation, a not-for-profit + corporation with a principal place of business in San Francisco, + California, as well as future copyleft versions of that license + published by that same organization. + + "Incorporate" means to publish or republish a Document, in whole or + in part, as part of another Document. + + An MMC is "eligible for relicensing" if it is licensed under this + License, and if all works that were first published under this + License somewhere other than this MMC, and subsequently + incorporated in whole or in part into the MMC, (1) had no cover + texts or invariant sections, and (2) were thus incorporated prior + to November 1, 2008. + + The operator of an MMC Site may republish an MMC contained in the + site under CC-BY-SA on the same site at any time before August 1, + 2009, provided the MMC is eligible for relicensing. + + +ADDENDUM: How to use this License for your documents +==================================================== + +To use this License in a document you have written, include a copy of +the License in the document and put the following copyright and license +notices just after the title page: + + Copyright (C) YEAR YOUR NAME. + Permission is granted to copy, distribute and/or modify this document + under the terms of the GNU Free Documentation License, Version 1.3 + or any later version published by the Free Software Foundation; + with no Invariant Sections, no Front-Cover Texts, and no Back-Cover + Texts. A copy of the license is included in the section entitled ``GNU + Free Documentation License''. + + If you have Invariant Sections, Front-Cover Texts and Back-Cover +Texts, replace the "with...Texts." line with this: + + with the Invariant Sections being LIST THEIR TITLES, with + the Front-Cover Texts being LIST, and with the Back-Cover Texts + being LIST. + + If you have Invariant Sections without Cover Texts, or some other +combination of the three, merge those two alternatives to suit the +situation. + + If your document contains nontrivial examples of program code, we +recommend releasing these examples in parallel under your choice of +free software license, such as the GNU General Public License, to +permit their use in free software. + + +File: libmicrohttpd-tutorial.info, Node: Example programs, Prev: License text, Up: Top + +Appendix C Example programs +*************************** + +* Menu: + +* hellobrowser.c:: +* logging.c:: +* responseheaders.c:: +* basicauthentication.c:: +* simplepost.c:: +* largepost.c:: +* sessions.c:: +* tlsauthentication.c:: + + +File: libmicrohttpd-tutorial.info, Node: hellobrowser.c, Next: logging.c, Up: Example programs + +C.1 hellobrowser.c +================== + + /* Feel free to use this example code in any way + you see fit (Public Domain) */ + + #include <sys/types.h> + #ifndef _WIN32 + #include <sys/select.h> + #include <sys/socket.h> + #else + #include <winsock2.h> + #endif + #include <string.h> + #include <microhttpd.h> + #include <stdio.h> + + #define PORT 8888 + + static int + answer_to_connection (void *cls, struct MHD_Connection *connection, + const char *url, const char *method, + const char *version, const char *upload_data, + size_t *upload_data_size, void **con_cls) + { + const char *page = "<html><body>Hello, browser!</body></html>"; + struct MHD_Response *response; + int ret; + + response = + MHD_create_response_from_buffer (strlen (page), (void *) page, + MHD_RESPMEM_PERSISTENT); + ret = MHD_queue_response (connection, MHD_HTTP_OK, response); + MHD_destroy_response (response); + + return ret; + } + + + int + main () + { + struct MHD_Daemon *daemon; + + daemon = MHD_start_daemon (MHD_USE_SELECT_INTERNALLY, PORT, NULL, NULL, + &answer_to_connection, NULL, MHD_OPTION_END); + if (NULL == daemon) + return 1; + + (void) getchar (); + + MHD_stop_daemon (daemon); + return 0; + } + + +File: libmicrohttpd-tutorial.info, Node: logging.c, Next: responseheaders.c, Prev: hellobrowser.c, Up: Example programs + +C.2 logging.c +============= + + /* Feel free to use this example code in any way + you see fit (Public Domain) */ + + #include <sys/types.h> + #ifndef _WIN32 + #include <sys/select.h> + #include <sys/socket.h> + #else + #include <winsock2.h> + #endif + #include <microhttpd.h> + #include <stdio.h> + + #define PORT 8888 + + + static int + print_out_key (void *cls, enum MHD_ValueKind kind, const char *key, + const char *value) + { + printf ("%s: %s\n", key, value); + return MHD_YES; + } + + + static int + answer_to_connection (void *cls, struct MHD_Connection *connection, + const char *url, const char *method, + const char *version, const char *upload_data, + size_t *upload_data_size, void **con_cls) + { + printf ("New %s request for %s using version %s\n", method, url, version); + + MHD_get_connection_values (connection, MHD_HEADER_KIND, print_out_key, + NULL); + + return MHD_NO; + } + + + int + main () + { + struct MHD_Daemon *daemon; + + daemon = MHD_start_daemon (MHD_USE_SELECT_INTERNALLY, PORT, NULL, NULL, + &answer_to_connection, NULL, MHD_OPTION_END); + if (NULL == daemon) + return 1; + + (void) getchar (); + + MHD_stop_daemon (daemon); + return 0; + } + + +File: libmicrohttpd-tutorial.info, Node: responseheaders.c, Next: basicauthentication.c, Prev: logging.c, Up: Example programs + +C.3 responseheaders.c +===================== + + /* Feel free to use this example code in any way + you see fit (Public Domain) */ + + #include <sys/types.h> + #ifndef _WIN32 + #include <sys/select.h> + #include <sys/socket.h> + #else + #include <winsock2.h> + #endif + #include <microhttpd.h> + #include <time.h> + #include <sys/stat.h> + #include <fcntl.h> + #include <string.h> + #include <stdio.h> + + #define PORT 8888 + #define FILENAME "picture.png" + #define MIMETYPE "image/png" + + static int + answer_to_connection (void *cls, struct MHD_Connection *connection, + const char *url, const char *method, + const char *version, const char *upload_data, + size_t *upload_data_size, void **con_cls) + { + struct MHD_Response *response; + int fd; + int ret; + struct stat sbuf; + + if (0 != strcmp (method, "GET")) + return MHD_NO; + + if ( (-1 == (fd = open (FILENAME, O_RDONLY))) || + (0 != fstat (fd, &sbuf)) ) + { + /* error accessing file */ + if (fd != -1) + (void) close (fd); + const char *errorstr = + "<html><body>An internal server error has occured!\ + </body></html>"; + response = + MHD_create_response_from_buffer (strlen (errorstr), + (void *) errorstr, + MHD_RESPMEM_PERSISTENT); + if (NULL != response) + { + ret = + MHD_queue_response (connection, MHD_HTTP_INTERNAL_SERVER_ERROR, + response); + MHD_destroy_response (response); + + return ret; + } + else + return MHD_NO; + } + response = + MHD_create_response_from_fd_at_offset (sbuf.st_size, fd, 0); + MHD_add_response_header (response, "Content-Type", MIMETYPE); + ret = MHD_queue_response (connection, MHD_HTTP_OK, response); + MHD_destroy_response (response); + + return ret; + } + + + int + main () + { + struct MHD_Daemon *daemon; + + daemon = MHD_start_daemon (MHD_USE_SELECT_INTERNALLY, PORT, NULL, NULL, + &answer_to_connection, NULL, MHD_OPTION_END); + if (NULL == daemon) + return 1; + + (void) getchar (); + + MHD_stop_daemon (daemon); + + return 0; + } + + +File: libmicrohttpd-tutorial.info, Node: basicauthentication.c, Next: simplepost.c, Prev: responseheaders.c, Up: Example programs + +C.4 basicauthentication.c +========================= + + /* Feel free to use this example code in any way + you see fit (Public Domain) */ + + #include <sys/types.h> + #ifndef _WIN32 + #include <sys/select.h> + #include <sys/socket.h> + #else + #include <winsock2.h> + #endif + #include <microhttpd.h> + #include <time.h> + #include <string.h> + #include <stdlib.h> + #include <stdio.h> + + #define PORT 8888 + + + static int + answer_to_connection (void *cls, struct MHD_Connection *connection, + const char *url, const char *method, + const char *version, const char *upload_data, + size_t *upload_data_size, void **con_cls) + { + char *user; + char *pass; + int fail; + int ret; + struct MHD_Response *response; + + if (0 != strcmp (method, "GET")) + return MHD_NO; + if (NULL == *con_cls) + { + *con_cls = connection; + return MHD_YES; + } + pass = NULL; + user = MHD_basic_auth_get_username_password (connection, &pass); + fail = ( (user == NULL) || + (0 != strcmp (user, "root")) || + (0 != strcmp (pass, "pa$$w0rd") ) ); + if (user != NULL) free (user); + if (pass != NULL) free (pass); + if (fail) + { + const char *page = "<html><body>Go away.</body></html>"; + response = + MHD_create_response_from_buffer (strlen (page), (void *) page, + MHD_RESPMEM_PERSISTENT); + ret = MHD_queue_basic_auth_fail_response (connection, + "my realm", + response); + } + else + { + const char *page = "<html><body>A secret.</body></html>"; + response = + MHD_create_response_from_buffer (strlen (page), (void *) page, + MHD_RESPMEM_PERSISTENT); + ret = MHD_queue_response (connection, MHD_HTTP_OK, response); + } + MHD_destroy_response (response); + return ret; + } + + + int + main () + { + struct MHD_Daemon *daemon; + + daemon = MHD_start_daemon (MHD_USE_SELECT_INTERNALLY, PORT, NULL, NULL, + &answer_to_connection, NULL, MHD_OPTION_END); + if (NULL == daemon) + return 1; + + (void) getchar (); + + MHD_stop_daemon (daemon); + return 0; + } + + +File: libmicrohttpd-tutorial.info, Node: simplepost.c, Next: largepost.c, Prev: basicauthentication.c, Up: Example programs + +C.5 simplepost.c +================ + + /* Feel free to use this example code in any way + you see fit (Public Domain) */ + + #include <sys/types.h> + #ifndef _WIN32 + #include <sys/select.h> + #include <sys/socket.h> + #else + #include <winsock2.h> + #endif + #include <microhttpd.h> + #include <stdio.h> + #include <string.h> + #include <stdlib.h> + + #define PORT 8888 + #define POSTBUFFERSIZE 512 + #define MAXNAMESIZE 20 + #define MAXANSWERSIZE 512 + + #define GET 0 + #define POST 1 + + struct connection_info_struct + { + int connectiontype; + char *answerstring; + struct MHD_PostProcessor *postprocessor; + }; + + const char *askpage = "<html><body>\ + What's your name, Sir?<br>\ + <form action=\"/namepost\" method=\"post\">\ + <input name=\"name\" type=\"text\"\ + <input type=\"submit\" value=\" Send \"></form>\ + </body></html>"; + + const char *greetingpage = + "<html><body><h1>Welcome, %s!</center></h1></body></html>"; + + const char *errorpage = + "<html><body>This doesn't seem to be right.</body></html>"; + + + static int + send_page (struct MHD_Connection *connection, const char *page) + { + int ret; + struct MHD_Response *response; + + + response = + MHD_create_response_from_buffer (strlen (page), (void *) page, + MHD_RESPMEM_PERSISTENT); + if (!response) + return MHD_NO; + + ret = MHD_queue_response (connection, MHD_HTTP_OK, response); + MHD_destroy_response (response); + + return ret; + } + + + static int + iterate_post (void *coninfo_cls, enum MHD_ValueKind kind, const char *key, + const char *filename, const char *content_type, + const char *transfer_encoding, const char *data, uint64_t off, + size_t size) + { + struct connection_info_struct *con_info = coninfo_cls; + + if (0 == strcmp (key, "name")) + { + if ((size > 0) && (size <= MAXNAMESIZE)) + { + char *answerstring; + answerstring = malloc (MAXANSWERSIZE); + if (!answerstring) + return MHD_NO; + + snprintf (answerstring, MAXANSWERSIZE, greetingpage, data); + con_info->answerstring = answerstring; + } + else + con_info->answerstring = NULL; + + return MHD_NO; + } + + return MHD_YES; + } + + static void + request_completed (void *cls, struct MHD_Connection *connection, + void **con_cls, enum MHD_RequestTerminationCode toe) + { + struct connection_info_struct *con_info = *con_cls; + + if (NULL == con_info) + return; + + if (con_info->connectiontype == POST) + { + MHD_destroy_post_processor (con_info->postprocessor); + if (con_info->answerstring) + free (con_info->answerstring); + } + + free (con_info); + *con_cls = NULL; + } + + + static int + answer_to_connection (void *cls, struct MHD_Connection *connection, + const char *url, const char *method, + const char *version, const char *upload_data, + size_t *upload_data_size, void **con_cls) + { + if (NULL == *con_cls) + { + struct connection_info_struct *con_info; + + con_info = malloc (sizeof (struct connection_info_struct)); + if (NULL == con_info) + return MHD_NO; + con_info->answerstring = NULL; + + if (0 == strcmp (method, "POST")) + { + con_info->postprocessor = + MHD_create_post_processor (connection, POSTBUFFERSIZE, + iterate_post, (void *) con_info); + + if (NULL == con_info->postprocessor) + { + free (con_info); + return MHD_NO; + } + + con_info->connectiontype = POST; + } + else + con_info->connectiontype = GET; + + *con_cls = (void *) con_info; + + return MHD_YES; + } + + if (0 == strcmp (method, "GET")) + { + return send_page (connection, askpage); + } + + if (0 == strcmp (method, "POST")) + { + struct connection_info_struct *con_info = *con_cls; + + if (*upload_data_size != 0) + { + MHD_post_process (con_info->postprocessor, upload_data, + *upload_data_size); + *upload_data_size = 0; + + return MHD_YES; + } + else if (NULL != con_info->answerstring) + return send_page (connection, con_info->answerstring); + } + + return send_page (connection, errorpage); + } + + int + main () + { + struct MHD_Daemon *daemon; + + daemon = MHD_start_daemon (MHD_USE_SELECT_INTERNALLY, PORT, NULL, NULL, + &answer_to_connection, NULL, + MHD_OPTION_NOTIFY_COMPLETED, request_completed, + NULL, MHD_OPTION_END); + if (NULL == daemon) + return 1; + + (void) getchar (); + + MHD_stop_daemon (daemon); + + return 0; + } + + +File: libmicrohttpd-tutorial.info, Node: largepost.c, Next: sessions.c, Prev: simplepost.c, Up: Example programs + +C.6 largepost.c +=============== + + /* Feel free to use this example code in any way + you see fit (Public Domain) */ + + #include <sys/types.h> + #ifndef _WIN32 + #include <sys/select.h> + #include <sys/socket.h> + #else + #include <winsock2.h> + #endif + #include <stdio.h> + #include <stdlib.h> + #include <string.h> + #include <microhttpd.h> + + #define PORT 8888 + #define POSTBUFFERSIZE 512 + #define MAXCLIENTS 2 + + #define GET 0 + #define POST 1 + + static unsigned int nr_of_uploading_clients = 0; + + struct connection_info_struct + { + int connectiontype; + struct MHD_PostProcessor *postprocessor; + FILE *fp; + const char *answerstring; + int answercode; + }; + + const char *askpage = "<html><body>\n\ + Upload a file, please!<br>\n\ + There are %u clients uploading at the moment.<br>\n\ + <form action=\"/filepost\" method=\"post\" enctype=\"multipart/form-data\">\n\ + <input name=\"file\" type=\"file\">\n\ + <input type=\"submit\" value=\" Send \"></form>\n\ + </body></html>"; + + const char *busypage = + "<html><body>This server is busy, please try again later.</body></html>"; + + const char *completepage = + "<html><body>The upload has been completed.</body></html>"; + + const char *errorpage = + "<html><body>This doesn't seem to be right.</body></html>"; + const char *servererrorpage = + "<html><body>An internal server error has occured.</body></html>"; + const char *fileexistspage = + "<html><body>This file already exists.</body></html>"; + + + static int + send_page (struct MHD_Connection *connection, const char *page, + int status_code) + { + int ret; + struct MHD_Response *response; + + response = + MHD_create_response_from_buffer (strlen (page), (void *) page, + MHD_RESPMEM_MUST_COPY); + if (!response) + return MHD_NO; + MHD_add_response_header (response, MHD_HTTP_HEADER_CONTENT_TYPE, "text/html"); + ret = MHD_queue_response (connection, status_code, response); + MHD_destroy_response (response); + + return ret; + } + + + static int + iterate_post (void *coninfo_cls, enum MHD_ValueKind kind, const char *key, + const char *filename, const char *content_type, + const char *transfer_encoding, const char *data, uint64_t off, + size_t size) + { + struct connection_info_struct *con_info = coninfo_cls; + FILE *fp; + + con_info->answerstring = servererrorpage; + con_info->answercode = MHD_HTTP_INTERNAL_SERVER_ERROR; + + if (0 != strcmp (key, "file")) + return MHD_NO; + + if (!con_info->fp) + { + if (NULL != (fp = fopen (filename, "rb"))) + { + fclose (fp); + con_info->answerstring = fileexistspage; + con_info->answercode = MHD_HTTP_FORBIDDEN; + return MHD_NO; + } + + con_info->fp = fopen (filename, "ab"); + if (!con_info->fp) + return MHD_NO; + } + + if (size > 0) + { + if (!fwrite (data, size, sizeof (char), con_info->fp)) + return MHD_NO; + } + + con_info->answerstring = completepage; + con_info->answercode = MHD_HTTP_OK; + + return MHD_YES; + } + + + static void + request_completed (void *cls, struct MHD_Connection *connection, + void **con_cls, enum MHD_RequestTerminationCode toe) + { + struct connection_info_struct *con_info = *con_cls; + + if (NULL == con_info) + return; + + if (con_info->connectiontype == POST) + { + if (NULL != con_info->postprocessor) + { + MHD_destroy_post_processor (con_info->postprocessor); + nr_of_uploading_clients--; + } + + if (con_info->fp) + fclose (con_info->fp); + } + + free (con_info); + *con_cls = NULL; + } + + + static int + answer_to_connection (void *cls, struct MHD_Connection *connection, + const char *url, const char *method, + const char *version, const char *upload_data, + size_t *upload_data_size, void **con_cls) + { + if (NULL == *con_cls) + { + struct connection_info_struct *con_info; + + if (nr_of_uploading_clients >= MAXCLIENTS) + return send_page (connection, busypage, MHD_HTTP_SERVICE_UNAVAILABLE); + + con_info = malloc (sizeof (struct connection_info_struct)); + if (NULL == con_info) + return MHD_NO; + + con_info->fp = NULL; + + if (0 == strcmp (method, "POST")) + { + con_info->postprocessor = + MHD_create_post_processor (connection, POSTBUFFERSIZE, + iterate_post, (void *) con_info); + + if (NULL == con_info->postprocessor) + { + free (con_info); + return MHD_NO; + } + + nr_of_uploading_clients++; + + con_info->connectiontype = POST; + con_info->answercode = MHD_HTTP_OK; + con_info->answerstring = completepage; + } + else + con_info->connectiontype = GET; + + *con_cls = (void *) con_info; + + return MHD_YES; + } + + if (0 == strcmp (method, "GET")) + { + char buffer[1024]; + + snprintf (buffer, sizeof (buffer), askpage, nr_of_uploading_clients); + return send_page (connection, buffer, MHD_HTTP_OK); + } + + if (0 == strcmp (method, "POST")) + { + struct connection_info_struct *con_info = *con_cls; + + if (0 != *upload_data_size) + { + MHD_post_process (con_info->postprocessor, upload_data, + *upload_data_size); + *upload_data_size = 0; + + return MHD_YES; + } + else + { + if (NULL != con_info->fp) + { + fclose (con_info->fp); + con_info->fp = NULL; + } + /* Now it is safe to open and inspect the file before calling send_page with a response */ + return send_page (connection, con_info->answerstring, + con_info->answercode); + } + + } + + return send_page (connection, errorpage, MHD_HTTP_BAD_REQUEST); + } + + + int + main () + { + struct MHD_Daemon *daemon; + + daemon = MHD_start_daemon (MHD_USE_SELECT_INTERNALLY, PORT, NULL, NULL, + &answer_to_connection, NULL, + MHD_OPTION_NOTIFY_COMPLETED, request_completed, + NULL, MHD_OPTION_END); + if (NULL == daemon) + return 1; + (void) getchar (); + MHD_stop_daemon (daemon); + return 0; + } + + +File: libmicrohttpd-tutorial.info, Node: sessions.c, Next: tlsauthentication.c, Prev: largepost.c, Up: Example programs + +C.7 sessions.c +============== + + /* Feel free to use this example code in any way + you see fit (Public Domain) */ + + /* needed for asprintf */ + #define _GNU_SOURCE + + #include <stdlib.h> + #include <string.h> + #include <stdio.h> + #include <errno.h> + #include <time.h> + #include <microhttpd.h> + + #if defined _WIN32 && !defined(__MINGW64_VERSION_MAJOR) + static int + asprintf (char **resultp, const char *format, ...) + { + va_list argptr; + char *result = NULL; + int len = 0; + + if (format == NULL) + return -1; + + va_start (argptr, format); + + len = _vscprintf ((char *) format, argptr); + if (len >= 0) + { + len += 1; + result = (char *) malloc (sizeof (char *) * len); + if (result != NULL) + { + int len2 = _vscprintf ((char *) format, argptr); + if (len2 != len - 1 || len2 <= 0) + { + free (result); + result = NULL; + len = -1; + } + else + { + len = len2; + if (resultp) + *resultp = result; + } + } + } + va_end (argptr); + return len; + } + #endif + + /** + * Invalid method page. + */ + #define METHOD_ERROR "<html><head><title>Illegal request</title></head><body>Go away.</body></html>" + + /** + * Invalid URL page. + */ + #define NOT_FOUND_ERROR "<html><head><title>Not found</title></head><body>Go away.</body></html>" + + /** + * Front page. (/) + */ + #define MAIN_PAGE "<html><head><title>Welcome</title></head><body><form action=\"/2\" method=\"post\">What is your name? <input type=\"text\" name=\"v1\" value=\"%s\" /><input type=\"submit\" value=\"Next\" /></body></html>" + + /** + * Second page. (/2) + */ + #define SECOND_PAGE "<html><head><title>Tell me more</title></head><body><a href=\"/\">previous</a> <form action=\"/S\" method=\"post\">%s, what is your job? <input type=\"text\" name=\"v2\" value=\"%s\" /><input type=\"submit\" value=\"Next\" /></body></html>" + + /** + * Second page (/S) + */ + #define SUBMIT_PAGE "<html><head><title>Ready to submit?</title></head><body><form action=\"/F\" method=\"post\"><a href=\"/2\">previous </a> <input type=\"hidden\" name=\"DONE\" value=\"yes\" /><input type=\"submit\" value=\"Submit\" /></body></html>" + + /** + * Last page. + */ + #define LAST_PAGE "<html><head><title>Thank you</title></head><body>Thank you.</body></html>" + + /** + * Name of our cookie. + */ + #define COOKIE_NAME "session" + + + /** + * State we keep for each user/session/browser. + */ + struct Session + { + /** + * We keep all sessions in a linked list. + */ + struct Session *next; + + /** + * Unique ID for this session. + */ + char sid[33]; + + /** + * Reference counter giving the number of connections + * currently using this session. + */ + unsigned int rc; + + /** + * Time when this session was last active. + */ + time_t start; + + /** + * String submitted via form. + */ + char value_1[64]; + + /** + * Another value submitted via form. + */ + char value_2[64]; + + }; + + + /** + * Data kept per request. + */ + struct Request + { + + /** + * Associated session. + */ + struct Session *session; + + /** + * Post processor handling form data (IF this is + * a POST request). + */ + struct MHD_PostProcessor *pp; + + /** + * URL to serve in response to this POST (if this request + * was a 'POST') + */ + const char *post_url; + + }; + + + /** + * Linked list of all active sessions. Yes, O(n) but a + * hash table would be overkill for a simple example... + */ + static struct Session *sessions; + + + + + /** + * Return the session handle for this connection, or + * create one if this is a new user. + */ + static struct Session * + get_session (struct MHD_Connection *connection) + { + struct Session *ret; + const char *cookie; + + cookie = MHD_lookup_connection_value (connection, + MHD_COOKIE_KIND, + COOKIE_NAME); + if (cookie != NULL) + { + /* find existing session */ + ret = sessions; + while (NULL != ret) + { + if (0 == strcmp (cookie, ret->sid)) + break; + ret = ret->next; + } + if (NULL != ret) + { + ret->rc++; + return ret; + } + } + /* create fresh session */ + ret = calloc (1, sizeof (struct Session)); + if (NULL == ret) + { + fprintf (stderr, "calloc error: %s\n", strerror (errno)); + return NULL; + } + /* not a super-secure way to generate a random session ID, + but should do for a simple example... */ + snprintf (ret->sid, + sizeof (ret->sid), + "%X%X%X%X", + (unsigned int) rand (), + (unsigned int) rand (), + (unsigned int) rand (), + (unsigned int) rand ()); + ret->rc++; + ret->start = time (NULL); + ret->next = sessions; + sessions = ret; + return ret; + } + + + /** + * Type of handler that generates a reply. + * + * @param cls content for the page (handler-specific) + * @param mime mime type to use + * @param session session information + * @param connection connection to process + * @param MHD_YES on success, MHD_NO on failure + */ + typedef int (*PageHandler)(const void *cls, + const char *mime, + struct Session *session, + struct MHD_Connection *connection); + + + /** + * Entry we generate for each page served. + */ + struct Page + { + /** + * Acceptable URL for this page. + */ + const char *url; + + /** + * Mime type to set for the page. + */ + const char *mime; + + /** + * Handler to call to generate response. + */ + PageHandler handler; + + /** + * Extra argument to handler. + */ + const void *handler_cls; + }; + + + /** + * Add header to response to set a session cookie. + * + * @param session session to use + * @param response response to modify + */ + static void + add_session_cookie (struct Session *session, + struct MHD_Response *response) + { + char cstr[256]; + snprintf (cstr, + sizeof (cstr), + "%s=%s", + COOKIE_NAME, + session->sid); + if (MHD_NO == + MHD_add_response_header (response, + MHD_HTTP_HEADER_SET_COOKIE, + cstr)) + { + fprintf (stderr, + "Failed to set session cookie header!\n"); + } + } + + + /** + * Handler that returns a simple static HTTP page that + * is passed in via 'cls'. + * + * @param cls a 'const char *' with the HTML webpage to return + * @param mime mime type to use + * @param session session handle + * @param connection connection to use + */ + static int + serve_simple_form (const void *cls, + const char *mime, + struct Session *session, + struct MHD_Connection *connection) + { + int ret; + const char *form = cls; + struct MHD_Response *response; + + /* return static form */ + response = MHD_create_response_from_buffer (strlen (form), + (void *) form, + MHD_RESPMEM_PERSISTENT); + add_session_cookie (session, response); + MHD_add_response_header (response, + MHD_HTTP_HEADER_CONTENT_ENCODING, + mime); + ret = MHD_queue_response (connection, + MHD_HTTP_OK, + response); + MHD_destroy_response (response); + return ret; + } + + + /** + * Handler that adds the 'v1' value to the given HTML code. + * + * @param cls a 'const char *' with the HTML webpage to return + * @param mime mime type to use + * @param session session handle + * @param connection connection to use + */ + static int + fill_v1_form (const void *cls, + const char *mime, + struct Session *session, + struct MHD_Connection *connection) + { + int ret; + const char *form = cls; + char *reply; + struct MHD_Response *response; + + if (-1 == asprintf (&reply, + form, + session->value_1)) + { + /* oops */ + return MHD_NO; + } + /* return static form */ + response = MHD_create_response_from_buffer (strlen (reply), + (void *) reply, + MHD_RESPMEM_MUST_FREE); + add_session_cookie (session, response); + MHD_add_response_header (response, + MHD_HTTP_HEADER_CONTENT_ENCODING, + mime); + ret = MHD_queue_response (connection, + MHD_HTTP_OK, + response); + MHD_destroy_response (response); + return ret; + } + + + /** + * Handler that adds the 'v1' and 'v2' values to the given HTML code. + * + * @param cls a 'const char *' with the HTML webpage to return + * @param mime mime type to use + * @param session session handle + * @param connection connection to use + */ + static int + fill_v1_v2_form (const void *cls, + const char *mime, + struct Session *session, + struct MHD_Connection *connection) + { + int ret; + const char *form = cls; + char *reply; + struct MHD_Response *response; + + if (-1 == asprintf (&reply, + form, + session->value_1, + session->value_2)) + { + /* oops */ + return MHD_NO; + } + /* return static form */ + response = MHD_create_response_from_buffer (strlen (reply), + (void *) reply, + MHD_RESPMEM_MUST_FREE); + add_session_cookie (session, response); + MHD_add_response_header (response, + MHD_HTTP_HEADER_CONTENT_ENCODING, + mime); + ret = MHD_queue_response (connection, + MHD_HTTP_OK, + response); + MHD_destroy_response (response); + return ret; + } + + + /** + * Handler used to generate a 404 reply. + * + * @param cls a 'const char *' with the HTML webpage to return + * @param mime mime type to use + * @param session session handle + * @param connection connection to use + */ + static int + not_found_page (const void *cls, + const char *mime, + struct Session *session, + struct MHD_Connection *connection) + { + int ret; + struct MHD_Response *response; + + /* unsupported HTTP method */ + response = MHD_create_response_from_buffer (strlen (NOT_FOUND_ERROR), + (void *) NOT_FOUND_ERROR, + MHD_RESPMEM_PERSISTENT); + ret = MHD_queue_response (connection, + MHD_HTTP_NOT_FOUND, + response); + MHD_add_response_header (response, + MHD_HTTP_HEADER_CONTENT_ENCODING, + mime); + MHD_destroy_response (response); + return ret; + } + + + /** + * List of all pages served by this HTTP server. + */ + static struct Page pages[] = + { + { "/", "text/html", &fill_v1_form, MAIN_PAGE }, + { "/2", "text/html", &fill_v1_v2_form, SECOND_PAGE }, + { "/S", "text/html", &serve_simple_form, SUBMIT_PAGE }, + { "/F", "text/html", &serve_simple_form, LAST_PAGE }, + { NULL, NULL, ¬_found_page, NULL } /* 404 */ + }; + + + + /** + * Iterator over key-value pairs where the value + * maybe made available in increments and/or may + * not be zero-terminated. Used for processing + * POST data. + * + * @param cls user-specified closure + * @param kind type of the value + * @param key 0-terminated key for the value + * @param filename name of the uploaded file, NULL if not known + * @param content_type mime-type of the data, NULL if not known + * @param transfer_encoding encoding of the data, NULL if not known + * @param data pointer to size bytes of data at the + * specified offset + * @param off offset of data in the overall value + * @param size number of bytes in data available + * @return MHD_YES to continue iterating, + * MHD_NO to abort the iteration + */ + static int + post_iterator (void *cls, + enum MHD_ValueKind kind, + const char *key, + const char *filename, + const char *content_type, + const char *transfer_encoding, + const char *data, uint64_t off, size_t size) + { + struct Request *request = cls; + struct Session *session = request->session; + + if (0 == strcmp ("DONE", key)) + { + fprintf (stdout, + "Session `%s' submitted `%s', `%s'\n", + session->sid, + session->value_1, + session->value_2); + return MHD_YES; + } + if (0 == strcmp ("v1", key)) + { + if (size + off > sizeof(session->value_1)) + size = sizeof (session->value_1) - off; + memcpy (&session->value_1[off], + data, + size); + if (size + off < sizeof (session->value_1)) + session->value_1[size+off] = '\0'; + return MHD_YES; + } + if (0 == strcmp ("v2", key)) + { + if (size + off > sizeof(session->value_2)) + size = sizeof (session->value_2) - off; + memcpy (&session->value_2[off], + data, + size); + if (size + off < sizeof (session->value_2)) + session->value_2[size+off] = '\0'; + return MHD_YES; + } + fprintf (stderr, "Unsupported form value `%s'\n", key); + return MHD_YES; + } + + + /** + * Main MHD callback for handling requests. + * + * + * @param cls argument given together with the function + * pointer when the handler was registered with MHD + * @param connection handle to connection which is being processed + * @param url the requested url + * @param method the HTTP method used ("GET", "PUT", etc.) + * @param version the HTTP version string (i.e. "HTTP/1.1") + * @param upload_data the data being uploaded (excluding HEADERS, + * for a POST that fits into memory and that is encoded + * with a supported encoding, the POST data will NOT be + * given in upload_data and is instead available as + * part of MHD_get_connection_values; very large POST + * data *will* be made available incrementally in + * upload_data) + * @param upload_data_size set initially to the size of the + * upload_data provided; the method must update this + * value to the number of bytes NOT processed; + * @param ptr pointer that the callback can set to some + * address and that will be preserved by MHD for future + * calls for this request; since the access handler may + * be called many times (i.e., for a PUT/POST operation + * with plenty of upload data) this allows the application + * to easily associate some request-specific state. + * If necessary, this state can be cleaned up in the + * global "MHD_RequestCompleted" callback (which + * can be set with the MHD_OPTION_NOTIFY_COMPLETED). + * Initially, <tt>*con_cls</tt> will be NULL. + * @return MHS_YES if the connection was handled successfully, + * MHS_NO if the socket must be closed due to a serios + * error while handling the request + */ + static int + create_response (void *cls, + struct MHD_Connection *connection, + const char *url, + const char *method, + const char *version, + const char *upload_data, + size_t *upload_data_size, + void **ptr) + { + struct MHD_Response *response; + struct Request *request; + struct Session *session; + int ret; + unsigned int i; + + request = *ptr; + if (NULL == request) + { + request = calloc (1, sizeof (struct Request)); + if (NULL == request) + { + fprintf (stderr, "calloc error: %s\n", strerror (errno)); + return MHD_NO; + } + *ptr = request; + if (0 == strcmp (method, MHD_HTTP_METHOD_POST)) + { + request->pp = MHD_create_post_processor (connection, 1024, + &post_iterator, request); + if (NULL == request->pp) + { + fprintf (stderr, "Failed to setup post processor for `%s'\n", + url); + return MHD_NO; /* internal error */ + } + } + return MHD_YES; + } + if (NULL == request->session) + { + request->session = get_session (connection); + if (NULL == request->session) + { + fprintf (stderr, "Failed to setup session for `%s'\n", + url); + return MHD_NO; /* internal error */ + } + } + session = request->session; + session->start = time (NULL); + if (0 == strcmp (method, MHD_HTTP_METHOD_POST)) + { + /* evaluate POST data */ + MHD_post_process (request->pp, + upload_data, + *upload_data_size); + if (0 != *upload_data_size) + { + *upload_data_size = 0; + return MHD_YES; + } + /* done with POST data, serve response */ + MHD_destroy_post_processor (request->pp); + request->pp = NULL; + method = MHD_HTTP_METHOD_GET; /* fake 'GET' */ + if (NULL != request->post_url) + url = request->post_url; + } + + if ( (0 == strcmp (method, MHD_HTTP_METHOD_GET)) || + (0 == strcmp (method, MHD_HTTP_METHOD_HEAD)) ) + { + /* find out which page to serve */ + i=0; + while ( (pages[i].url != NULL) && + (0 != strcmp (pages[i].url, url)) ) + i++; + ret = pages[i].handler (pages[i].handler_cls, + pages[i].mime, + session, connection); + if (ret != MHD_YES) + fprintf (stderr, "Failed to create page for `%s'\n", + url); + return ret; + } + /* unsupported HTTP method */ + response = MHD_create_response_from_buffer (strlen (METHOD_ERROR), + (void *) METHOD_ERROR, + MHD_RESPMEM_PERSISTENT); + ret = MHD_queue_response (connection, + MHD_HTTP_METHOD_NOT_ACCEPTABLE, + response); + MHD_destroy_response (response); + return ret; + } + + + /** + * Callback called upon completion of a request. + * Decrements session reference counter. + * + * @param cls not used + * @param connection connection that completed + * @param con_cls session handle + * @param toe status code + */ + static void + request_completed_callback (void *cls, + struct MHD_Connection *connection, + void **con_cls, + enum MHD_RequestTerminationCode toe) + { + struct Request *request = *con_cls; + + if (NULL == request) + return; + if (NULL != request->session) + request->session->rc--; + if (NULL != request->pp) + MHD_destroy_post_processor (request->pp); + free (request); + } + + + /** + * Clean up handles of sessions that have been idle for + * too long. + */ + static void + expire_sessions () + { + struct Session *pos; + struct Session *prev; + struct Session *next; + time_t now; + + now = time (NULL); + prev = NULL; + pos = sessions; + while (NULL != pos) + { + next = pos->next; + if (now - pos->start > 60 * 60) + { + /* expire sessions after 1h */ + if (NULL == prev) + sessions = pos->next; + else + prev->next = next; + free (pos); + } + else + prev = pos; + pos = next; + } + } + + + /** + * Call with the port number as the only argument. + * Never terminates (other than by signals, such as CTRL-C). + */ + int + main (int argc, char *const *argv) + { + struct MHD_Daemon *d; + struct timeval tv; + struct timeval *tvp; + fd_set rs; + fd_set ws; + fd_set es; + int max; + MHD_UNSIGNED_LONG_LONG mhd_timeout; + + if (argc != 2) + { + printf ("%s PORT\n", argv[0]); + return 1; + } + /* initialize PRNG */ + srand ((unsigned int) time (NULL)); + d = MHD_start_daemon (MHD_USE_DEBUG, + atoi (argv[1]), + NULL, NULL, + &create_response, NULL, + MHD_OPTION_CONNECTION_TIMEOUT, (unsigned int) 15, + MHD_OPTION_NOTIFY_COMPLETED, &request_completed_callback, NULL, + MHD_OPTION_END); + if (NULL == d) + return 1; + while (1) + { + expire_sessions (); + max = 0; + FD_ZERO (&rs); + FD_ZERO (&ws); + FD_ZERO (&es); + if (MHD_YES != MHD_get_fdset (d, &rs, &ws, &es, &max)) + break; /* fatal internal error */ + if (MHD_get_timeout (d, &mhd_timeout) == MHD_YES) + { + tv.tv_sec = mhd_timeout / 1000; + tv.tv_usec = (mhd_timeout - (tv.tv_sec * 1000)) * 1000; + tvp = &tv; + } + else + tvp = NULL; + if (-1 == select (max + 1, &rs, &ws, &es, tvp)) + { + if (EINTR != errno) + fprintf (stderr, + "Aborting due to error during select: %s\n", + strerror (errno)); + break; + } + MHD_run (d); + } + MHD_stop_daemon (d); + return 0; + } + + +File: libmicrohttpd-tutorial.info, Node: tlsauthentication.c, Prev: sessions.c, Up: Example programs + +C.8 tlsauthentication.c +======================= + + /* Feel free to use this example code in any way + you see fit (Public Domain) */ + + #include <sys/types.h> + #ifndef _WIN32 + #include <sys/select.h> + #include <sys/socket.h> + #else + #include <winsock2.h> + #endif + #include <microhttpd.h> + #include <string.h> + #include <stdio.h> + #include <stdlib.h> + + #define PORT 8888 + + #define REALM "\"Maintenance\"" + #define USER "a legitimate user" + #define PASSWORD "and his password" + + #define SERVERKEYFILE "server.key" + #define SERVERCERTFILE "server.pem" + + + static char * + string_to_base64 (const char *message) + { + const char *lookup = + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; + unsigned long l; + int i; + char *tmp; + size_t length = strlen (message); + + tmp = malloc (length * 2); + if (NULL == tmp) + return tmp; + + tmp[0] = 0; + + for (i = 0; i < length; i += 3) + { + l = (((unsigned long) message[i]) << 16) + | (((i + 1) < length) ? (((unsigned long) message[i + 1]) << 8) : 0) + | (((i + 2) < length) ? ((unsigned long) message[i + 2]) : 0); + + + strncat (tmp, &lookup[(l >> 18) & 0x3F], 1); + strncat (tmp, &lookup[(l >> 12) & 0x3F], 1); + + if (i + 1 < length) + strncat (tmp, &lookup[(l >> 6) & 0x3F], 1); + if (i + 2 < length) + strncat (tmp, &lookup[l & 0x3F], 1); + } + + if (length % 3) + strncat (tmp, "===", 3 - length % 3); + + return tmp; + } + + + static long + get_file_size (const char *filename) + { + FILE *fp; + + fp = fopen (filename, "rb"); + if (fp) + { + long size; + + if ((0 != fseek (fp, 0, SEEK_END)) || (-1 == (size = ftell (fp)))) + size = 0; + + fclose (fp); + + return size; + } + else + return 0; + } + + static char * + load_file (const char *filename) + { + FILE *fp; + char *buffer; + long size; + + size = get_file_size (filename); + if (size == 0) + return NULL; + + fp = fopen (filename, "rb"); + if (!fp) + return NULL; + + buffer = malloc (size); + if (!buffer) + { + fclose (fp); + return NULL; + } + + if (size != fread (buffer, 1, size, fp)) + { + free (buffer); + buffer = NULL; + } + + fclose (fp); + return buffer; + } + + static int + ask_for_authentication (struct MHD_Connection *connection, const char *realm) + { + int ret; + struct MHD_Response *response; + char *headervalue; + const char *strbase = "Basic realm="; + + response = MHD_create_response_from_buffer (0, NULL, + MHD_RESPMEM_PERSISTENT); + if (!response) + return MHD_NO; + + headervalue = malloc (strlen (strbase) + strlen (realm) + 1); + if (!headervalue) + return MHD_NO; + + strcpy (headervalue, strbase); + strcat (headervalue, realm); + + ret = MHD_add_response_header (response, "WWW-Authenticate", headervalue); + free (headervalue); + if (!ret) + { + MHD_destroy_response (response); + return MHD_NO; + } + + ret = MHD_queue_response (connection, MHD_HTTP_UNAUTHORIZED, response); + + MHD_destroy_response (response); + + return ret; + } + + static int + is_authenticated (struct MHD_Connection *connection, + const char *username, const char *password) + { + const char *headervalue; + char *expected_b64, *expected; + const char *strbase = "Basic "; + int authenticated; + + headervalue = + MHD_lookup_connection_value (connection, MHD_HEADER_KIND, + "Authorization"); + if (NULL == headervalue) + return 0; + if (0 != strncmp (headervalue, strbase, strlen (strbase))) + return 0; + + expected = malloc (strlen (username) + 1 + strlen (password) + 1); + if (NULL == expected) + return 0; + + strcpy (expected, username); + strcat (expected, ":"); + strcat (expected, password); + + expected_b64 = string_to_base64 (expected); + free (expected); + if (NULL == expected_b64) + return 0; + + authenticated = + (strcmp (headervalue + strlen (strbase), expected_b64) == 0); + + free (expected_b64); + + return authenticated; + } + + + static int + secret_page (struct MHD_Connection *connection) + { + int ret; + struct MHD_Response *response; + const char *page = "<html><body>A secret.</body></html>"; + + response = + MHD_create_response_from_buffer (strlen (page), (void *) page, + MHD_RESPMEM_PERSISTENT); + if (!response) + return MHD_NO; + + ret = MHD_queue_response (connection, MHD_HTTP_OK, response); + MHD_destroy_response (response); + + return ret; + } + + + static int + answer_to_connection (void *cls, struct MHD_Connection *connection, + const char *url, const char *method, + const char *version, const char *upload_data, + size_t *upload_data_size, void **con_cls) + { + if (0 != strcmp (method, "GET")) + return MHD_NO; + if (NULL == *con_cls) + { + *con_cls = connection; + return MHD_YES; + } + + if (!is_authenticated (connection, USER, PASSWORD)) + return ask_for_authentication (connection, REALM); + + return secret_page (connection); + } + + + int + main () + { + struct MHD_Daemon *daemon; + char *key_pem; + char *cert_pem; + + key_pem = load_file (SERVERKEYFILE); + cert_pem = load_file (SERVERCERTFILE); + + if ((key_pem == NULL) || (cert_pem == NULL)) + { + printf ("The key/certificate files could not be read.\n"); + return 1; + } + + daemon = + MHD_start_daemon (MHD_USE_SELECT_INTERNALLY | MHD_USE_SSL, PORT, NULL, + NULL, &answer_to_connection, NULL, + MHD_OPTION_HTTPS_MEM_KEY, key_pem, + MHD_OPTION_HTTPS_MEM_CERT, cert_pem, MHD_OPTION_END); + if (NULL == daemon) + { + printf ("%s\n", cert_pem); + + free (key_pem); + free (cert_pem); + + return 1; + } + + (void) getchar (); + + MHD_stop_daemon (daemon); + free (key_pem); + free (cert_pem); + + return 0; + } + + + +Tag Table: +Node: Top866 +Node: Introduction1917 +Node: Hello browser example3223 +Node: Exploring requests14247 +Node: Response headers19643 +Node: Supporting basic authentication27522 +Node: Processing POST data34913 +Node: Improved processing of POST data43534 +Node: Session management54177 +Node: Adding a layer of security57072 +Node: Bibliography71602 +Node: License text72797 +Node: Example programs97972 +Node: hellobrowser.c98285 +Node: logging.c99828 +Node: responseheaders.c101411 +Node: basicauthentication.c104035 +Node: simplepost.c106574 +Node: largepost.c112254 +Node: sessions.c119619 +Node: tlsauthentication.c141964 + +End Tag Table |