X-Git-Url: http://www.project-moonshot.org/gitweb/?p=mech_eap.git;a=blobdiff_plain;f=libeap%2Fsrc%2Fwps%2Fwps_upnp.c;h=44318e09425204d8cf70fa8b2fecaec3baef1c9f;hp=97eec1d52440fca883fd94f3b10f22a5b1343dc2;hb=4f319dde67a76fe0aaf33f6d2788968012584ada;hpb=ed09b5e64dd485851310307979d5eed14678087b diff --git a/libeap/src/wps/wps_upnp.c b/libeap/src/wps/wps_upnp.c index 97eec1d..44318e0 100644 --- a/libeap/src/wps/wps_upnp.c +++ b/libeap/src/wps/wps_upnp.c @@ -47,7 +47,7 @@ * -- Needs renaming with module prefix to avoid polluting the debugger * namespace and causing possible collisions with other static fncs * and structure declarations when using the debugger. - * -- The http error code generation is pretty bogus, hopefully noone cares. + * -- The http error code generation is pretty bogus, hopefully no one cares. * * Author: Ted Merrill, Atheros Communications, based upon earlier work * as explained above and below. @@ -172,7 +172,7 @@ #include "includes.h" -#include +#include #include #include #include @@ -212,6 +212,10 @@ /* Maximum number of Probe Request events per second */ #define MAX_EVENTS_PER_SEC 5 + +static struct upnp_wps_device_sm *shared_upnp_device = NULL; + + /* Write the current date/time per RFC */ void format_date(struct wpabuf *buf) { @@ -223,6 +227,8 @@ void format_date(struct wpabuf *buf) t = time(NULL); date = gmtime(&t); + if (date == NULL) + return; wpabuf_printf(buf, "%s, %02d %s %d %02d:%02d:%02d GMT", &weekday_str[date->tm_wday * 4], date->tm_mday, &month_str[date->tm_mon * 4], date->tm_year + 1900, @@ -245,13 +251,16 @@ void format_date(struct wpabuf *buf) * use for constructing UUIDs for subscriptions. Presumably any method from * rfc4122 is good enough; I've chosen random number method. */ -static void uuid_make(u8 uuid[UUID_LEN]) +static int uuid_make(u8 uuid[UUID_LEN]) { - os_get_random(uuid, UUID_LEN); + if (os_get_random(uuid, UUID_LEN) < 0) + return -1; /* Replace certain bits as specified in rfc4122 or X.667 */ uuid[6] &= 0x0f; uuid[6] |= (4 << 4); /* version 4 == random gen */ uuid[8] &= 0x3f; uuid[8] |= 0x80; + + return 0; } @@ -301,15 +310,15 @@ static void subscr_addr_add_url(struct subscription *s, const char *url, int alloc_len; char *scratch_mem = NULL; char *mem; - char *domain_and_port; + char *host; char *delim; char *path; - char *domain; int port = 80; /* port to send to (default is port 80) */ struct addrinfo hints; struct addrinfo *result = NULL; struct addrinfo *rp; int rerr; + size_t host_len, path_len; /* url MUST begin with http: */ if (url_len < 7 || os_strncasecmp(url, "http://", 7)) @@ -317,30 +326,22 @@ static void subscr_addr_add_url(struct subscription *s, const char *url, url += 7; url_len -= 7; - /* allocate memory for the extra stuff we need */ - alloc_len = 2 * (url_len + 1); - scratch_mem = os_zalloc(alloc_len); + /* Make a copy of the string to allow modification during parsing */ + scratch_mem = dup_binstr(url, url_len); if (scratch_mem == NULL) goto fail; - mem = scratch_mem; - os_strncpy(mem, url, url_len); - wpa_printf(MSG_DEBUG, "WPS UPnP: Adding URL '%s'", mem); - domain_and_port = mem; - mem += 1 + os_strlen(mem); - delim = os_strchr(domain_and_port, '/'); - if (delim) { - *delim++ = 0; /* null terminate domain and port */ - path = delim; - } else { - path = domain_and_port + os_strlen(domain_and_port); - } - domain = mem; - strcpy(domain, domain_and_port); - delim = os_strchr(domain, ':'); + wpa_printf(MSG_DEBUG, "WPS UPnP: Adding URL '%s'", scratch_mem); + host = scratch_mem; + path = os_strchr(host, '/'); + if (path) + *path++ = '\0'; /* null terminate host */ + + /* Process and remove optional port component */ + delim = os_strchr(host, ':'); if (delim) { - *delim++ = 0; /* null terminate domain */ - if (isdigit(*delim)) - port = atol(delim); + *delim = '\0'; /* null terminate host name for now */ + if (isdigit(delim[1])) + port = atol(delim + 1); } /* @@ -363,13 +364,21 @@ static void subscr_addr_add_url(struct subscription *s, const char *url, hints.ai_flags = 0; #endif hints.ai_protocol = 0; /* Any protocol? */ - rerr = getaddrinfo(domain, NULL /* fill in port ourselves */, + rerr = getaddrinfo(host, NULL /* fill in port ourselves */, &hints, &result); if (rerr) { wpa_printf(MSG_INFO, "WPS UPnP: Resolve error %d (%s) on: %s", - rerr, gai_strerror(rerr), domain); + rerr, gai_strerror(rerr), host); goto fail; } + + if (delim) + *delim = ':'; /* Restore port */ + + host_len = os_strlen(host); + path_len = path ? os_strlen(path) : 0; + alloc_len = host_len + 1 + 1 + path_len + 1; + for (rp = result; rp; rp = rp->ai_next) { struct subscr_addr *a; @@ -382,16 +391,16 @@ static void subscr_addr_add_url(struct subscription *s, const char *url, a = os_zalloc(sizeof(*a) + alloc_len); if (a == NULL) - continue; - mem = (void *) (a + 1); + break; + mem = (char *) (a + 1); a->domain_and_port = mem; - strcpy(mem, domain_and_port); - mem += 1 + strlen(mem); + os_memcpy(mem, host, host_len); + mem += host_len + 1; a->path = mem; - if (path[0] != '/') + if (path == NULL || path[0] != '/') *mem++ = '/'; - strcpy(mem, path); - mem += 1 + os_strlen(mem); + if (path) + os_memcpy(mem, path, path_len); os_memcpy(&a->saddr, rp->ai_addr, sizeof(a->saddr)); a->saddr.sin_port = htons(port); @@ -428,23 +437,6 @@ static void subscr_addr_list_create(struct subscription *s, } -int send_wpabuf(int fd, struct wpabuf *buf) -{ - wpa_printf(MSG_DEBUG, "WPS UPnP: Send %lu byte message", - (unsigned long) wpabuf_len(buf)); - errno = 0; - if (write(fd, wpabuf_head(buf), wpabuf_len(buf)) != - (int) wpabuf_len(buf)) { - wpa_printf(MSG_ERROR, "WPS UPnP: Failed to send buffer: " - "errno=%d (%s)", - errno, strerror(errno)); - return -1; - } - - return 0; -} - - static void wpabuf_put_property(struct wpabuf *buf, const char *name, const char *value) { @@ -476,14 +468,14 @@ static void upnp_wps_device_send_event(struct upnp_wps_device_sm *sm) "\n" "\n"; const char *format_tail = "\n"; - struct os_time now; + struct os_reltime now; if (dl_list_empty(&sm->subscriptions)) { /* optimize */ return; } - if (os_get_time(&now) == 0) { + if (os_get_reltime(&now) == 0) { if (now.sec != sm->last_event_sec) { sm->last_event_sec = now.sec; sm->num_events_in_sec = 1; @@ -546,10 +538,13 @@ static void upnp_wps_device_send_event(struct upnp_wps_device_sm *sm) */ void subscription_destroy(struct subscription *s) { + struct upnp_wps_device_interface *iface; wpa_printf(MSG_DEBUG, "WPS UPnP: Destroy subscription %p", s); subscr_addr_free_all(s); event_delete_all(s); - upnp_er_remove_notification(s); + dl_list_for_each(iface, &s->sm->interfaces, + struct upnp_wps_device_interface, list) + upnp_er_remove_notification(iface->wps->registrar, s); os_free(s); } @@ -604,7 +599,10 @@ static struct wpabuf * build_fake_wsc_ack(void) wpabuf_put_be16(msg, ATTR_REGISTRAR_NONCE); wpabuf_put_be16(msg, WPS_NONCE_LEN); wpabuf_put(msg, WPS_NONCE_LEN); - wps_build_wfa_ext(msg, 0, NULL, 0); + if (wps_build_wfa_ext(msg, 0, NULL, 0)) { + wpabuf_free(msg); + return NULL; + } return msg; } @@ -697,6 +695,7 @@ struct subscription * subscription_start(struct upnp_wps_device_sm *sm, struct subscription *s; time_t now = time(NULL); time_t expire = now + UPNP_SUBSCRIBE_SEC; + char str[80]; /* Get rid of expired subscriptions so we have room */ subscription_list_age(sm, now); @@ -705,10 +704,12 @@ struct subscription * subscription_start(struct upnp_wps_device_sm *sm, if (dl_list_len(&sm->subscriptions) >= MAX_SUBSCRIPTIONS) { s = dl_list_first(&sm->subscriptions, struct subscription, list); - wpa_printf(MSG_INFO, "WPS UPnP: Too many subscriptions, " - "trashing oldest"); - dl_list_del(&s->list); - subscription_destroy(s); + if (s) { + wpa_printf(MSG_INFO, + "WPS UPnP: Too many subscriptions, trashing oldest"); + dl_list_del(&s->list); + subscription_destroy(s); + } } s = os_zalloc(sizeof(*s)); @@ -719,7 +720,10 @@ struct subscription * subscription_start(struct upnp_wps_device_sm *sm, s->sm = sm; s->timeout_time = expire; - uuid_make(s->uuid); + if (uuid_make(s->uuid) < 0) { + subscription_destroy(s); + return NULL; + } subscr_addr_list_create(s, callback_urls); if (dl_list_empty(&s->addr_list)) { wpa_printf(MSG_DEBUG, "WPS UPnP: No valid callback URLs in " @@ -740,8 +744,10 @@ struct subscription * subscription_start(struct upnp_wps_device_sm *sm, subscription_destroy(s); return NULL; } - wpa_printf(MSG_DEBUG, "WPS UPnP: Subscription %p started with %s", - s, callback_urls); + uuid_bin2str(s->uuid, str, sizeof(str)); + wpa_printf(MSG_DEBUG, + "WPS UPnP: Subscription %p (SID %s) started with %s", + s, str, callback_urls); /* Schedule sending this */ event_send_all_later(sm); return s; @@ -951,10 +957,13 @@ static void upnp_wps_free_msearchreply(struct dl_list *head) } -static void upnp_wps_free_subscriptions(struct dl_list *head) +static void upnp_wps_free_subscriptions(struct dl_list *head, + struct wps_registrar *reg) { struct subscription *s, *tmp; dl_list_for_each_safe(s, tmp, head, struct subscription, list) { + if (reg && s->reg != reg) + continue; dl_list_del(&s->list); subscription_destroy(s); } @@ -965,15 +974,16 @@ static void upnp_wps_free_subscriptions(struct dl_list *head) * upnp_wps_device_stop - Stop WPS UPnP operations on an interface * @sm: WPS UPnP state machine from upnp_wps_device_init() */ -void upnp_wps_device_stop(struct upnp_wps_device_sm *sm) +static void upnp_wps_device_stop(struct upnp_wps_device_sm *sm) { if (!sm || !sm->started) return; wpa_printf(MSG_DEBUG, "WPS UPnP: Stop device"); web_listener_stop(sm); + ssdp_listener_stop(sm); upnp_wps_free_msearchreply(&sm->msearch_replies); - upnp_wps_free_subscriptions(&sm->subscriptions); + upnp_wps_free_subscriptions(&sm->subscriptions, NULL); advertisement_state_machine_stop(sm, 1); @@ -985,7 +995,6 @@ void upnp_wps_device_stop(struct upnp_wps_device_sm *sm) if (sm->multicast_sd >= 0) close(sm->multicast_sd); sm->multicast_sd = -1; - ssdp_listener_stop(sm); sm->started = 0; } @@ -997,7 +1006,7 @@ void upnp_wps_device_stop(struct upnp_wps_device_sm *sm) * @net_if: Selected network interface name * Returns: 0 on success, -1 on failure */ -int upnp_wps_device_start(struct upnp_wps_device_sm *sm, char *net_if) +static int upnp_wps_device_start(struct upnp_wps_device_sm *sm, char *net_if) { if (!sm || !net_if) return -1; @@ -1052,24 +1061,59 @@ fail: } +static struct upnp_wps_device_interface * +upnp_wps_get_iface(struct upnp_wps_device_sm *sm, void *priv) +{ + struct upnp_wps_device_interface *iface; + dl_list_for_each(iface, &sm->interfaces, + struct upnp_wps_device_interface, list) { + if (iface->priv == priv) + return iface; + } + return NULL; +} + + /** * upnp_wps_device_deinit - Deinitialize WPS UPnP * @sm: WPS UPnP state machine from upnp_wps_device_init() + * @priv: External context data that was used in upnp_wps_device_init() call */ -void upnp_wps_device_deinit(struct upnp_wps_device_sm *sm) +void upnp_wps_device_deinit(struct upnp_wps_device_sm *sm, void *priv) { + struct upnp_wps_device_interface *iface; + if (!sm) return; - upnp_wps_device_stop(sm); - - if (sm->peer.wps) - wps_deinit(sm->peer.wps); - os_free(sm->root_dir); - os_free(sm->desc_url); - os_free(sm->ctx->ap_pin); - os_free(sm->ctx); - os_free(sm); + iface = upnp_wps_get_iface(sm, priv); + if (iface == NULL) { + wpa_printf(MSG_ERROR, "WPS UPnP: Could not find the interface " + "instance to deinit"); + return; + } + wpa_printf(MSG_DEBUG, "WPS UPnP: Deinit interface instance %p", iface); + if (dl_list_len(&sm->interfaces) == 1) { + wpa_printf(MSG_DEBUG, "WPS UPnP: Deinitializing last instance " + "- free global device instance"); + upnp_wps_device_stop(sm); + } else + upnp_wps_free_subscriptions(&sm->subscriptions, + iface->wps->registrar); + dl_list_del(&iface->list); + + if (iface->peer.wps) + wps_deinit(iface->peer.wps); + os_free(iface->ctx->ap_pin); + os_free(iface->ctx); + os_free(iface); + + if (dl_list_empty(&sm->interfaces)) { + os_free(sm->root_dir); + os_free(sm->desc_url); + os_free(sm); + shared_upnp_device = NULL; + } } @@ -1078,25 +1122,59 @@ void upnp_wps_device_deinit(struct upnp_wps_device_sm *sm) * @ctx: callback table; we must eventually free it * @wps: Pointer to longterm WPS context * @priv: External context data that will be used in callbacks + * @net_if: Selected network interface name * Returns: WPS UPnP state or %NULL on failure */ struct upnp_wps_device_sm * upnp_wps_device_init(struct upnp_wps_device_ctx *ctx, struct wps_context *wps, - void *priv) + void *priv, char *net_if) { struct upnp_wps_device_sm *sm; + struct upnp_wps_device_interface *iface; + int start = 0; - sm = os_zalloc(sizeof(*sm)); - if (!sm) { - wpa_printf(MSG_ERROR, "WPS UPnP: upnp_wps_device_init failed"); + iface = os_zalloc(sizeof(*iface)); + if (iface == NULL) { + os_free(ctx->ap_pin); + os_free(ctx); + return NULL; + } + wpa_printf(MSG_DEBUG, "WPS UPnP: Init interface instance %p", iface); + + iface->ctx = ctx; + iface->wps = wps; + iface->priv = priv; + + if (shared_upnp_device) { + wpa_printf(MSG_DEBUG, "WPS UPnP: Share existing device " + "context"); + sm = shared_upnp_device; + } else { + wpa_printf(MSG_DEBUG, "WPS UPnP: Initialize device context"); + sm = os_zalloc(sizeof(*sm)); + if (!sm) { + wpa_printf(MSG_ERROR, "WPS UPnP: upnp_wps_device_init " + "failed"); + os_free(iface); + os_free(ctx->ap_pin); + os_free(ctx); + return NULL; + } + shared_upnp_device = sm; + + dl_list_init(&sm->msearch_replies); + dl_list_init(&sm->subscriptions); + dl_list_init(&sm->interfaces); + start = 1; + } + + dl_list_add(&sm->interfaces, &iface->list); + + if (start && upnp_wps_device_start(sm, net_if)) { + upnp_wps_device_deinit(sm, priv); return NULL; } - sm->ctx = ctx; - sm->wps = wps; - sm->priv = priv; - dl_list_init(&sm->msearch_replies); - dl_list_init(&sm->subscriptions); return sm; } @@ -1115,16 +1193,20 @@ int upnp_wps_subscribers(struct upnp_wps_device_sm *sm) int upnp_wps_set_ap_pin(struct upnp_wps_device_sm *sm, const char *ap_pin) { + struct upnp_wps_device_interface *iface; if (sm == NULL) return 0; - os_free(sm->ctx->ap_pin); - if (ap_pin) { - sm->ctx->ap_pin = os_strdup(ap_pin); - if (sm->ctx->ap_pin == NULL) - return -1; - } else - sm->ctx->ap_pin = NULL; + dl_list_for_each(iface, &sm->interfaces, + struct upnp_wps_device_interface, list) { + os_free(iface->ctx->ap_pin); + if (ap_pin) { + iface->ctx->ap_pin = os_strdup(ap_pin); + if (iface->ctx->ap_pin == NULL) + return -1; + } else + iface->ctx->ap_pin = NULL; + } return 0; }