fragattacks/src/wps/http_server.c
Jouni Malinen e88060e1a7 HTTP server: Allow TCP socket to be reused
This makes it easier to handle cases where the application is restarted
and the previously used local TCP port may not have been fully cleared
in the network stack.

Signed-hostap: Jouni Malinen <jouni@qca.qualcomm.com>
2013-10-18 14:13:45 +03:00

311 lines
6.2 KiB
C

/*
* http_server - HTTP server
* Copyright (c) 2009, Jouni Malinen <j@w1.fi>
*
* This software may be distributed under the terms of the BSD license.
* See README for more details.
*/
#include "includes.h"
#include <fcntl.h>
#include "common.h"
#include "eloop.h"
#include "httpread.h"
#include "http_server.h"
#define HTTP_SERVER_TIMEOUT 30
#define HTTP_SERVER_MAX_REQ_LEN 8000
#define HTTP_SERVER_MAX_CONNECTIONS 10
struct http_request {
struct http_request *next;
struct http_server *srv;
int fd;
struct sockaddr_in cli;
struct httpread *hread;
};
struct http_server {
void (*cb)(void *ctx, struct http_request *req);
void *cb_ctx;
int fd;
int port;
struct http_request *requests;
unsigned int request_count;
};
static void http_request_cb(struct httpread *handle, void *cookie,
enum httpread_event en)
{
struct http_request *req = cookie;
struct http_server *srv = req->srv;
if (en == HTTPREAD_EVENT_FILE_READY) {
wpa_printf(MSG_DEBUG, "HTTP: Request from %s:%d received",
inet_ntoa(req->cli.sin_addr),
ntohs(req->cli.sin_port));
srv->cb(srv->cb_ctx, req);
return;
}
wpa_printf(MSG_DEBUG, "HTTP: Request from %s:%d could not be received "
"completely", inet_ntoa(req->cli.sin_addr),
ntohs(req->cli.sin_port));
http_request_deinit(req);
}
static struct http_request * http_request_init(struct http_server *srv, int fd,
struct sockaddr_in *cli)
{
struct http_request *req;
if (srv->request_count >= HTTP_SERVER_MAX_CONNECTIONS) {
wpa_printf(MSG_DEBUG, "HTTP: Too many concurrent requests");
return NULL;
}
req = os_zalloc(sizeof(*req));
if (req == NULL)
return NULL;
req->srv = srv;
req->fd = fd;
req->cli = *cli;
req->hread = httpread_create(req->fd, http_request_cb, req,
HTTP_SERVER_MAX_REQ_LEN,
HTTP_SERVER_TIMEOUT);
if (req->hread == NULL) {
http_request_deinit(req);
return NULL;
}
return req;
}
void http_request_deinit(struct http_request *req)
{
struct http_request *r, *p;
struct http_server *srv;
if (req == NULL)
return;
srv = req->srv;
p = NULL;
r = srv->requests;
while (r) {
if (r == req) {
if (p)
p->next = r->next;
else
srv->requests = r->next;
srv->request_count--;
break;
}
p = r;
r = r->next;
}
httpread_destroy(req->hread);
close(req->fd);
os_free(req);
}
static void http_request_free_all(struct http_request *req)
{
struct http_request *prev;
while (req) {
prev = req;
req = req->next;
http_request_deinit(prev);
}
}
void http_request_send(struct http_request *req, struct wpabuf *resp)
{
int res;
wpa_printf(MSG_DEBUG, "HTTP: Send %lu byte response to %s:%d",
(unsigned long) wpabuf_len(resp),
inet_ntoa(req->cli.sin_addr),
ntohs(req->cli.sin_port));
res = send(req->fd, wpabuf_head(resp), wpabuf_len(resp), 0);
if (res < 0) {
wpa_printf(MSG_DEBUG, "HTTP: Send failed: %s",
strerror(errno));
} else if ((size_t) res < wpabuf_len(resp)) {
wpa_printf(MSG_DEBUG, "HTTP: Sent only %d of %lu bytes",
res, (unsigned long) wpabuf_len(resp));
/* TODO: add eloop handler for sending rest of the data */
}
wpabuf_free(resp);
}
void http_request_send_and_deinit(struct http_request *req,
struct wpabuf *resp)
{
http_request_send(req, resp);
http_request_deinit(req);
}
enum httpread_hdr_type http_request_get_type(struct http_request *req)
{
return httpread_hdr_type_get(req->hread);
}
char * http_request_get_uri(struct http_request *req)
{
return httpread_uri_get(req->hread);
}
char * http_request_get_hdr(struct http_request *req)
{
return httpread_hdr_get(req->hread);
}
char * http_request_get_data(struct http_request *req)
{
return httpread_data_get(req->hread);
}
char * http_request_get_hdr_line(struct http_request *req, const char *tag)
{
return httpread_hdr_line_get(req->hread, tag);
}
struct sockaddr_in * http_request_get_cli_addr(struct http_request *req)
{
return &req->cli;
}
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;
struct http_request *req;
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));
req = http_request_init(srv, conn, &addr);
if (req == NULL) {
close(conn);
return;
}
req->next = srv->requests;
srv->requests = req;
srv->request_count++;
}
struct http_server * http_server_init(struct in_addr *addr, int port,
void (*cb)(void *ctx,
struct http_request *req),
void *cb_ctx)
{
struct sockaddr_in sin;
struct http_server *srv;
int on = 1;
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;
setsockopt(srv->fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));
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);
}
http_request_free_all(srv->requests);
os_free(srv);
}
int http_server_get_port(struct http_server *srv)
{
return srv->port;
}