wps_upnp_web.c

来自「最新的Host AP 新添加了许多pcmcia 的驱动」· C语言 代码 · 共 1,965 行 · 第 1/4 页

C
1,965
字号
		h = end + 1;		end = os_strchr(h, '\n');		if (end == NULL)			break; /* no unterminated lines allowed */		/* NT assures that it is our type of subscription;		 * not used for a renewl.		 **/		match = "NT:";		match_len = os_strlen(match);		if (os_strncasecmp(h, match, match_len) == 0) {			h += match_len;			while (*h == ' ' || *h == '\t')				h++;			match = "upnp:event";			match_len = os_strlen(match);			if (os_strncasecmp(h, match, match_len) != 0) {				ret = HTTP_BAD_REQUEST;				goto error;			}			got_nt = 1;			continue;		}		/* HOST should refer to us */#if 0		match = "HOST:";		match_len = os_strlen(match);		if (os_strncasecmp(h, match, match_len) == 0) {			h += match_len;			while (*h == ' ' || *h == '\t')				h++;			.....		}#endif		/* CALLBACK gives one or more URLs for NOTIFYs		 * to be sent as a result of the subscription.		 * Each URL is enclosed in angle brackets.		 */		match = "CALLBACK:";		match_len = os_strlen(match);		if (os_strncasecmp(h, match, match_len) == 0) {			h += match_len;			while (*h == ' ' || *h == '\t')				h++;			len = end - h;			os_free(callback_urls);			callback_urls = os_malloc(len + 1);			if (callback_urls == NULL) {				ret = HTTP_INTERNAL_SERVER_ERROR;				goto error;			}			os_memcpy(callback_urls, h, len);			callback_urls[len] = 0;			continue;		}		/* SID is only for renewal */		match = "SID:";		match_len = os_strlen(match);		if (os_strncasecmp(h, match, match_len) == 0) {			h += match_len;			while (*h == ' ' || *h == '\t')				h++;			match = "uuid:";			match_len = os_strlen(match);			if (os_strncasecmp(h, match, match_len) != 0) {				ret = HTTP_BAD_REQUEST;				goto error;			}			h += match_len;			while (*h == ' ' || *h == '\t')				h++;			if (uuid_str2bin(h, uuid)) {				ret = HTTP_BAD_REQUEST;				goto error;			}			got_uuid = 1;			continue;		}		/* TIMEOUT is requested timeout, but apparently we can		 * just ignore this.		 */	}	if (got_uuid) {		/* renewal */		if (callback_urls) {			ret = HTTP_BAD_REQUEST;			goto error;		}		s = subscription_renew(sm, uuid);		if (s == NULL) {			ret = HTTP_PRECONDITION_FAILED;			goto error;		}	} else if (callback_urls) {		if (!got_nt) {			ret = HTTP_PRECONDITION_FAILED;			goto error;		}		s = subscription_start(sm, callback_urls);		if (s == NULL) {			ret = HTTP_INTERNAL_SERVER_ERROR;			goto error;		}		callback_urls = NULL;   /* is now owned by subscription */	} else {		ret = HTTP_PRECONDITION_FAILED;		goto error;	}	/* success */	http_put_reply_code(buf, HTTP_OK);	wpabuf_put_str(buf, http_server_hdr);	wpabuf_put_str(buf, http_connection_close);	wpabuf_put_str(buf, "Content-Length: 0\r\n");	wpabuf_put_str(buf, "SID: uuid:");	/* subscription id */	b = wpabuf_put(buf, 0);	uuid_bin2str(s->uuid, b, 80);	wpabuf_put(buf, os_strlen(b));	wpabuf_put_str(buf, "\r\n");	wpabuf_printf(buf, "Timeout: Second-%d\r\n", UPNP_SUBSCRIBE_SEC);	http_put_date(buf);	/* And empty line to terminate header: */	wpabuf_put_str(buf, "\r\n");	send_wpabuf(c->sd, buf);	wpabuf_free(buf);	os_free(callback_urls);	return;error:	/* Per UPnP spec:	* Errors	* Incompatible headers	*   400 Bad Request. If SID header and one of NT or CALLBACK headers	*     are present, the publisher must respond with HTTP error	*     400 Bad Request.	* Missing or invalid CALLBACK	*   412 Precondition Failed. If CALLBACK header is missing or does not	*     contain a valid HTTP URL, the publisher must respond with HTTP	*     error 412 Precondition Failed.	* Invalid NT	*   412 Precondition Failed. If NT header does not equal upnp:event,	*     the publisher must respond with HTTP error 412 Precondition	*     Failed.	* [For resubscription, use 412 if unknown uuid].	* Unable to accept subscription	*   5xx. If a publisher is not able to accept a subscription (such as	*     due to insufficient resources), it must respond with a	*     HTTP 500-series error code.	*   599 Too many subscriptions (not a standard HTTP error)	*/	http_put_empty(buf, ret);	send_wpabuf(c->sd, buf);	wpabuf_free(buf);}/* Given that we have received a header w/ UNSUBSCRIBE, act upon it * * Format of UNSUBSCRIBE (case-insensitive): * * First line must be: *      UNSUBSCRIBE /wps_event HTTP/1.1 * * Our response (if no error) which includes only required lines is: * HTTP/1.1 200 OK * Content-Length: 0 * * Header lines must end with \r\n * Per RFC 2616, content-length: is not required but connection:close * would appear to be required (given that we will be closing it!). */static void web_connection_parse_unsubscribe(struct web_connection *c,					     const char *filename){	struct upnp_wps_device_sm *sm = c->sm;	struct wpabuf *buf;	char *hdr = httpread_hdr_get(c->hread);	char *h;	char *match;	int match_len;	char *end;	u8 uuid[UUID_LEN];	int got_uuid = 0;	struct subscription *s = NULL;	enum http_reply_code ret = HTTP_INTERNAL_SERVER_ERROR;	/* Parse/validate headers */	h = hdr;	/* First line: UNSUBSCRIBE /wps_event HTTP/1.1	 * has already been parsed.	 */	if (os_strcasecmp(filename, UPNP_WPS_DEVICE_EVENT_FILE) != 0) {		ret = HTTP_PRECONDITION_FAILED;		goto send_msg;	}	wpa_printf(MSG_DEBUG, "WPS UPnP: HTTP UNSUBSCRIBE for event");	end = os_strchr(h, '\n');	for (; end != NULL; h = end + 1) {		/* Option line by option line */		h = end + 1;		end = os_strchr(h, '\n');		if (end == NULL)			break; /* no unterminated lines allowed */		/* HOST should refer to us */#if 0		match = "HOST:";		match_len = os_strlen(match);		if (os_strncasecmp(h, match, match_len) == 0) {			h += match_len;			while (*h == ' ' || *h == '\t')				h++;			.....		}#endif		/* SID is only for renewal */		match = "SID:";		match_len = os_strlen(match);		if (os_strncasecmp(h, match, match_len) == 0) {			h += match_len;			while (*h == ' ' || *h == '\t')				h++;			match = "uuid:";			match_len = os_strlen(match);			if (os_strncasecmp(h, match, match_len) != 0) {				ret = HTTP_BAD_REQUEST;				goto send_msg;			}			h += match_len;			while (*h == ' ' || *h == '\t')				h++;			if (uuid_str2bin(h, uuid)) {				ret = HTTP_BAD_REQUEST;				goto send_msg;			}			got_uuid = 1;			continue;		}	}	if (got_uuid) {		s = subscription_find(sm, uuid);		if (s) {			wpa_printf(MSG_DEBUG, "WPS UPnP: Unsubscribing %p %s",				   s,				   (s && s->addr_list &&				    s->addr_list->domain_and_port) ?				   s->addr_list->domain_and_port : "-null-");			subscription_unlink(s);			subscription_destroy(s);		}	} else {		wpa_printf(MSG_INFO, "WPS UPnP: Unsubscribe fails (not "			   "found)");		ret = HTTP_PRECONDITION_FAILED;		goto send_msg;	}	ret = HTTP_OK;send_msg:	buf = wpabuf_alloc(200);	if (buf == NULL)		return;	http_put_empty(buf, ret);	send_wpabuf(c->sd, buf);	wpabuf_free(buf);}/* Send error in response to unknown requests */static void web_connection_unimplemented(struct web_connection *c){	struct wpabuf *buf;	buf = wpabuf_alloc(200);	if (buf == NULL)		return;	http_put_empty(buf, HTTP_UNIMPLEMENTED);	send_wpabuf(c->sd, buf);	wpabuf_free(buf);}/* Called when we have gotten an apparently valid http request. */static void web_connection_check_data(struct web_connection *c){	struct httpread *hread = c->hread;	enum httpread_hdr_type htype = httpread_hdr_type_get(hread);	/* char *data = httpread_data_get(hread); */	char *filename = httpread_uri_get(hread);	c->done = 1;	if (!filename) {		wpa_printf(MSG_INFO, "WPS UPnP: Could not get HTTP URI");		return;	}	/* Trim leading slashes from filename */	while (*filename == '/')		filename++;	wpa_printf(MSG_DEBUG, "WPS UPnP: Got HTTP request type %d from %s:%d",		   htype, inet_ntoa(c->cli_addr.sin_addr),		   htons(c->cli_addr.sin_port));	switch (htype) {	case HTTPREAD_HDR_TYPE_GET:		web_connection_parse_get(c, filename);		break;	case HTTPREAD_HDR_TYPE_POST:		web_connection_parse_post(c, filename);		break;	case HTTPREAD_HDR_TYPE_SUBSCRIBE:		web_connection_parse_subscribe(c, filename);		break;	case HTTPREAD_HDR_TYPE_UNSUBSCRIBE:		web_connection_parse_unsubscribe(c, filename);		break;		/* We are not required to support M-POST; just plain		 * POST is supposed to work, so we only support that.		 * If for some reason we need to support M-POST, it is		 * mostly the same as POST, with small differences.		 */	default:		/* Send 501 for anything else */		web_connection_unimplemented(c);		break;	}}/* called back when we have gotten request */static void web_connection_got_file_handler(struct httpread *handle,					    void *cookie,					    enum httpread_event en){	struct web_connection *c = cookie;	if (en == HTTPREAD_EVENT_FILE_READY)		web_connection_check_data(c);	web_connection_stop(c);}/* web_connection_start - Start web connection * @sm: WPS UPnP state machine from upnp_wps_device_init() * @sd: Socket descriptor * @addr: Client address * * 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){	struct web_connection *c = NULL;	/* if too many connections, bail */	if (sm->n_web_connections >= MAX_WEB_CONNECTIONS) {		close(sd);		return;	}	c = os_zalloc(sizeof(*c));	if (c == NULL)		return;	os_memcpy(&c->cli_addr, addr, sizeof(c->cli_addr));	c->sm = sm;	c->sd = sd;#if 0	/*	 * Setting non-blocking should not be necessary for read, and can mess	 * up sending where blocking might be better.	 */	if (fcntl(sd, F_SETFL, O_NONBLOCK) != 0)		break;#endif	c->hread = httpread_create(c->sd, web_connection_got_file_handler,				   c /* cookie */,				   WEB_CONNECTION_MAX_READ,				   WEB_CONNECTION_TIMEOUT_SEC);	if (c->hread == NULL)		goto fail;	if (sm->web_connections) {		c->next = sm->web_connections;		c->prev = c->next->prev;		c->prev->next = c;		c->next->prev = c;	} else {		sm->web_connections = c->next = c->prev = c;	}	sm->n_web_connections++;	return;fail:	if (c)		web_connection_stop(c);}/* * Listening for web connections * We have a single TCP listening port, and hand off connections as we get * them. */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);}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 */	web_listener_stop(sm);	return -1;}

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?