From b905c4a398fcfb0951f92473254451a26adcb142 Mon Sep 17 00:00:00 2001 From: Jouni Malinen Date: Sun, 8 Nov 2009 17:26:55 +0200 Subject: [PATCH] WPS: Add HTTP server module Clean up code so that UPnP implementation does not need to include all the HTTP functionality. In addition, make it easier to share HTTP server functionality with other components in the future. --- hostapd/Makefile | 1 + src/wps/http_server.c | 128 ++++++++++++++++++++++++++++++++++++++++ src/wps/http_server.h | 27 +++++++++ src/wps/wps_upnp_i.h | 3 +- src/wps/wps_upnp_web.c | 81 ++++--------------------- wpa_supplicant/Makefile | 1 + 6 files changed, 170 insertions(+), 71 deletions(-) create mode 100644 src/wps/http_server.c create mode 100644 src/wps/http_server.h diff --git a/hostapd/Makefile b/hostapd/Makefile index 6756c8fd4..1b6748d05 100644 --- a/hostapd/Makefile +++ b/hostapd/Makefile @@ -312,6 +312,7 @@ OBJS += ../src/wps/wps_upnp_event.o OBJS += ../src/wps/upnp_xml.o OBJS += ../src/wps/httpread.o OBJS += ../src/wps/http_client.o +OBJS += ../src/wps/http_server.o endif endif diff --git a/src/wps/http_server.c b/src/wps/http_server.c new file mode 100644 index 000000000..2593dda8e --- /dev/null +++ b/src/wps/http_server.c @@ -0,0 +1,128 @@ +/** + * http_server - HTTP server + * Copyright (c) 2009, Jouni Malinen + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#include "includes.h" +#include + +#include "common.h" +#include "eloop.h" +#include "http_server.h" + + +struct http_server { + void (*cb)(void *ctx, int fd, struct sockaddr_in *addr); + void *cb_ctx; + + int fd; + int port; +}; + + +static void http_server_cb(int sd, void *eloop_ctx, void *sock_ctx) +{ + struct sockaddr_in addr; + socklen_t addr_len = sizeof(addr); + struct http_server *srv = eloop_ctx; + int conn; + + conn = accept(srv->fd, (struct sockaddr *) &addr, &addr_len); + if (conn < 0) { + wpa_printf(MSG_DEBUG, "HTTP: Failed to accept new connection: " + "%s", strerror(errno)); + return; + } + wpa_printf(MSG_DEBUG, "HTTP: Connection from %s:%d", + inet_ntoa(addr.sin_addr), ntohs(addr.sin_port)); + srv->cb(srv->cb_ctx, conn, &addr); +} + + +struct http_server * http_server_init(struct in_addr *addr, int port, + void (*cb)(void *ctx, int fd, + struct sockaddr_in *addr), + void *cb_ctx) +{ + struct sockaddr_in sin; + struct http_server *srv; + + srv = os_zalloc(sizeof(*srv)); + if (srv == NULL) + return NULL; + srv->cb = cb; + srv->cb_ctx = cb_ctx; + + srv->fd = socket(AF_INET, SOCK_STREAM, 0); + if (srv->fd < 0) + goto fail; + if (fcntl(srv->fd, F_SETFL, O_NONBLOCK) < 0) + goto fail; + if (port < 0) + srv->port = 49152; + else + srv->port = port; + + os_memset(&sin, 0, sizeof(sin)); + sin.sin_family = AF_INET; + sin.sin_addr.s_addr = addr->s_addr; + + for (;;) { + sin.sin_port = htons(srv->port); + if (bind(srv->fd, (struct sockaddr *) &sin, sizeof(sin)) == 0) + break; + if (errno == EADDRINUSE) { + /* search for unused port */ + if (++srv->port == 65535 || port >= 0) + goto fail; + continue; + } + wpa_printf(MSG_DEBUG, "HTTP: Failed to bind server port %d: " + "%s", srv->port, strerror(errno)); + goto fail; + } + if (listen(srv->fd, 10 /* max backlog */) < 0) + goto fail; + if (fcntl(srv->fd, F_SETFL, O_NONBLOCK) < 0) + goto fail; + if (eloop_register_sock(srv->fd, EVENT_TYPE_READ, http_server_cb, + srv, NULL)) + goto fail; + + wpa_printf(MSG_DEBUG, "HTTP: Started server on %s:%d", + inet_ntoa(*addr), srv->port); + + return srv; + +fail: + http_server_deinit(srv); + return NULL; +} + + +void http_server_deinit(struct http_server *srv) +{ + if (srv == NULL) + return; + if (srv->fd >= 0) { + eloop_unregister_sock(srv->fd, EVENT_TYPE_READ); + close(srv->fd); + } + + os_free(srv); +} + + +int http_server_get_port(struct http_server *srv) +{ + return srv->port; +} diff --git a/src/wps/http_server.h b/src/wps/http_server.h new file mode 100644 index 000000000..1d01efca2 --- /dev/null +++ b/src/wps/http_server.h @@ -0,0 +1,27 @@ +/** + * http_server - HTTP server + * Copyright (c) 2009, Jouni Malinen + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#ifndef HTTP_SERVER_H +#define HTTP_SERVER_H + +struct http_server; + +struct http_server * http_server_init(struct in_addr *addr, int port, + void (*cb)(void *ctx, int fd, + struct sockaddr_in *addr), + void *cb_ctx); +void http_server_deinit(struct http_server *srv); +int http_server_get_port(struct http_server *srv); + +#endif /* HTTP_SERVER_H */ diff --git a/src/wps/wps_upnp_i.h b/src/wps/wps_upnp_i.h index 00b71d702..02b807e57 100644 --- a/src/wps/wps_upnp_i.h +++ b/src/wps/wps_upnp_i.h @@ -130,8 +130,7 @@ struct upnp_wps_device_sm { struct advertisement_state_machine *msearch_replies; int n_msearch_replies; /* no. of pending M-SEARCH replies */ int web_port; /* our port that others get xml files from */ - int web_sd; /* socket to listen for web requests */ - int web_sd_registered; /* nonzero if we must cancel registration */ + struct http_server *web_srv; struct web_connection *web_connections; /* linked list */ int n_web_connections; /* no. of pending web connections */ /* Note: subscriptions are kept in expiry order */ diff --git a/src/wps/wps_upnp_web.c b/src/wps/wps_upnp_web.c index 2ff3af269..a841c13cd 100644 --- a/src/wps/wps_upnp_web.c +++ b/src/wps/wps_upnp_web.c @@ -9,13 +9,12 @@ */ #include "includes.h" -#include #include "common.h" #include "base64.h" -#include "eloop.h" #include "uuid.h" #include "httpread.h" +#include "http_server.h" #include "wps_i.h" #include "wps_upnp.h" #include "wps_upnp_i.h" @@ -1593,9 +1592,9 @@ static void web_connection_got_file_handler(struct httpread *handle, * The socket descriptor sd is handed over for ownership by the WPS UPnP * state machine. */ -static void web_connection_start(struct upnp_wps_device_sm *sm, - int sd, struct sockaddr_in *addr) +static void web_connection_start(void *ctx, int sd, struct sockaddr_in *addr) { + struct upnp_wps_device_sm *sm = ctx; struct web_connection *c = NULL; /* if too many connections, bail */ @@ -1649,77 +1648,21 @@ fail: void web_listener_stop(struct upnp_wps_device_sm *sm) { - if (sm->web_sd_registered) { - sm->web_sd_registered = 0; - eloop_unregister_sock(sm->web_sd, EVENT_TYPE_READ); - } - if (sm->web_sd >= 0) - close(sm->web_sd); - sm->web_sd = -1; -} - - -static void web_listener_handler(int sd, void *eloop_ctx, void *sock_ctx) -{ - struct sockaddr_in addr; - socklen_t addr_len = sizeof(addr); - struct upnp_wps_device_sm *sm = sock_ctx; - int new_sd; - - /* Create state for new connection */ - /* Remember so we can cancel if need be */ - new_sd = accept(sm->web_sd, (struct sockaddr *) &addr, &addr_len); - if (new_sd < 0) { - wpa_printf(MSG_ERROR, "WPS UPnP: web listener accept " - "errno=%d (%s) web_sd=%d", - errno, strerror(errno), sm->web_sd); - return; - } - web_connection_start(sm, new_sd, &addr); + http_server_deinit(sm->web_srv); + sm->web_srv = NULL; } int web_listener_start(struct upnp_wps_device_sm *sm) { - struct sockaddr_in addr; - int port; - - sm->web_sd = socket(AF_INET, SOCK_STREAM, 0); - if (sm->web_sd < 0) - goto fail; - if (fcntl(sm->web_sd, F_SETFL, O_NONBLOCK) != 0) - goto fail; - port = 49152; /* first non-reserved port */ - for (;;) { - os_memset(&addr, 0, sizeof(addr)); - addr.sin_family = AF_INET; - addr.sin_addr.s_addr = sm->ip_addr; - addr.sin_port = htons(port); - if (bind(sm->web_sd, (struct sockaddr *) &addr, - sizeof(addr)) == 0) - break; - if (errno == EADDRINUSE) { - /* search for unused port */ - if (++port == 65535) - goto fail; - continue; - } - goto fail; + struct in_addr addr; + addr.s_addr = sm->ip_addr; + sm->web_srv = http_server_init(&addr, -1, web_connection_start, sm); + if (sm->web_srv == NULL) { + web_listener_stop(sm); + return -1; } - if (listen(sm->web_sd, 10 /* max backlog */) != 0) - goto fail; - if (fcntl(sm->web_sd, F_SETFL, O_NONBLOCK) != 0) - goto fail; - if (eloop_register_sock(sm->web_sd, EVENT_TYPE_READ, - web_listener_handler, NULL, sm)) - goto fail; - sm->web_sd_registered = 1; - sm->web_port = port; + sm->web_port = http_server_get_port(sm->web_srv); return 0; - -fail: - /* Error */ - web_listener_stop(sm); - return -1; } diff --git a/wpa_supplicant/Makefile b/wpa_supplicant/Makefile index 70e7a3a6b..6436f3986 100644 --- a/wpa_supplicant/Makefile +++ b/wpa_supplicant/Makefile @@ -512,6 +512,7 @@ OBJS += ../src/wps/wps_upnp_event.o OBJS += ../src/wps/upnp_xml.o OBJS += ../src/wps/httpread.o OBJS += ../src/wps/http_client.o +OBJS += ../src/wps/http_server.o endif endif