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.
This commit is contained in:
Jouni Malinen 2009-11-08 17:26:55 +02:00
parent 585774f28a
commit b905c4a398
6 changed files with 170 additions and 71 deletions

View file

@ -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

128
src/wps/http_server.c Normal file
View file

@ -0,0 +1,128 @@
/**
* http_server - HTTP server
* Copyright (c) 2009, Jouni Malinen <j@w1.fi>
*
* 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 <fcntl.h>
#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;
}

27
src/wps/http_server.h Normal file
View file

@ -0,0 +1,27 @@
/**
* http_server - HTTP server
* Copyright (c) 2009, Jouni Malinen <j@w1.fi>
*
* 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 */

View file

@ -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 */

View file

@ -9,13 +9,12 @@
*/
#include "includes.h"
#include <fcntl.h>
#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;
}
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;
return 0;
fail:
/* Error */
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;
}
sm->web_port = http_server_get_port(sm->web_srv);
return 0;
}

View file

@ -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