/** * event_retry - Called when we had a failure delivering event msg * @e: Event * @do_next_address: skip address e.g. on connect fail */ static void event_retry(struct wps_event_ *e, int do_next_address) { struct subscription *s = e->s; struct upnp_wps_device_sm *sm = s->sm; wpa_printf(MSG_DEBUG, "WPS UPnP: Retry event %p for subscription %p", e, s); event_clean(e); /* will set: s->current_event = NULL; */ if (do_next_address) { e->retry++; wpa_printf(MSG_DEBUG, "WPS UPnP: Try address %d", e->retry); } if (e->retry >= dl_list_len(&s->addr_list)) { wpa_printf(MSG_DEBUG, "WPS UPnP: Giving up on sending event " "for %s", e->addr->domain_and_port); event_delete(e); s->last_event_failed = 1; if (!dl_list_empty(&s->event_queue)) event_send_all_later(s->sm); return; } dl_list_add(&s->event_queue, &e->list); event_send_all_later(sm); }
/** * event_add - Add a new event to a queue * @s: Subscription * @data: Event data (is copied; caller retains ownership) * Returns: 0 on success, 1 on error */ int event_add(struct subscription *s, const struct wpabuf *data) { struct wps_event_ *e; if (s->n_queue >= MAX_EVENTS_QUEUED) { wpa_printf(MSG_DEBUG, "WPS UPnP: Too many events queued for " "subscriber"); return 1; } e = os_zalloc(sizeof(*e)); if (e == NULL) return 1; e->s = s; e->sd = -1; e->data = wpabuf_dup(data); if (e->data == NULL) { os_free(e); return 1; } e->subscriber_sequence = s->next_subscriber_sequence++; if (s->next_subscriber_sequence == 0) s->next_subscriber_sequence++; event_enqueue_at_end(s, e); event_send_all_later(s->sm); return 0; }
/* event_send_all_later_handler -- actually send events as needed */ void event_send_all_later_handler(void *eloop_data, void *user_ctx) { struct upnp_wps_device_sm *sm = user_ctx; struct subscription *s; struct subscription *s_old; int nerrors = 0; sm->event_send_all_queued = 0; s = sm->subscriptions; if (s == NULL) return; do { if (s->addr_list == NULL) { /* if we've given up on all addresses */ wpa_printf(MSG_DEBUG, "WPS UPnP: Removing " "subscription with no addresses"); s_old = s; s = s_old->next; subscription_unlink(s_old); subscription_destroy(s_old); } else { if (s->current_event == NULL /* not busy */ && s->event_queue != NULL /* more to do */) { if (event_send_start(s)) nerrors++; } s = s->next; } } while (sm->subscriptions != NULL && s != sm->subscriptions); if (nerrors) { /* Try again later */ event_send_all_later(sm); } }
static void event_http_cb(void *ctx, struct http_client *c, enum http_client_event event) { struct wps_event_ *e = ctx; struct subscription *s = e->s; wpa_printf(MSG_DEBUG, "WPS UPnP: HTTP client callback: e=%p c=%p " "event=%d", e, c, event); switch (event) { case HTTP_CLIENT_OK: wpa_printf(MSG_DEBUG, "WPS UPnP: Got event %p reply OK from %s", e, e->addr->domain_and_port); e->addr->num_failures = 0; s->last_event_failed = 0; event_delete(e); /* Schedule sending more if there is more to send */ if (!dl_list_empty(&s->event_queue)) event_send_all_later(s->sm); break; case HTTP_CLIENT_FAILED: wpa_printf(MSG_DEBUG, "WPS UPnP: Event send failure"); event_addr_failure(e); break; case HTTP_CLIENT_INVALID_REPLY: wpa_printf(MSG_DEBUG, "WPS UPnP: Invalid reply"); event_addr_failure(e); break; case HTTP_CLIENT_TIMEOUT: wpa_printf(MSG_DEBUG, "WPS UPnP: Event send timeout"); event_addr_failure(e); break; } }
/* event_got_response_handler -- called back when http response is received. */ static void event_got_response_handler(struct httpread *handle, void *cookie, enum httpread_event en) { struct wps_event_ *e = cookie; struct subscription *s = e->s; struct upnp_wps_device_sm *sm = s->sm; struct httpread *hread = e->hread; int reply_code = 0; assert(e == s->current_event); eloop_cancel_timeout(event_timeout_handler, NULL, e); if (en == HTTPREAD_EVENT_FILE_READY) { if (httpread_hdr_type_get(hread) == HTTPREAD_HDR_TYPE_REPLY) { reply_code = httpread_reply_code_get(hread); if (reply_code == HTTP_OK) { wpa_printf(MSG_DEBUG, "WPS UPnP: Got event reply OK from " "%s", e->addr->domain_and_port); event_delete(e); goto send_more; } else { wpa_printf(MSG_DEBUG, "WPS UPnP: Got event " "error reply code %d from %s", reply_code, e->addr->domain_and_port); goto bad; } } else { wpa_printf(MSG_DEBUG, "WPS UPnP: Got bogus event " "response %d from %s", en, e->addr->domain_and_port); } } else { wpa_printf(MSG_DEBUG, "WPS UPnP: Event response timeout/fail " "for %s", e->addr->domain_and_port); goto bad; } event_retry(e, 1); goto send_more; send_more: /* Schedule sending more if there is more to send */ if (s->event_queue) event_send_all_later(sm); return; bad: /* * If other side doesn't like what we say, forget about them. * (There is no way to tell other side that we are dropping * them...). * Alternately, we could just do event_delete(e) */ wpa_printf(MSG_DEBUG, "WPS UPnP: Deleting subscription due to errors"); subscription_unlink(s); subscription_destroy(s); }
/** * event_add - Add a new event to a queue * @s: Subscription * @data: Event data (is copied; caller retains ownership) * @probereq: Whether this is a Probe Request event * Returns: 0 on success, -1 on error, 1 on max event queue limit reached */ int event_add(struct subscription *s, const struct wpabuf *data, int probereq) { struct wps_event_ *e; unsigned int len; len = dl_list_len(&s->event_queue); if (len >= MAX_EVENTS_QUEUED) { wpa_printf(MSG_DEBUG, "WPS UPnP: Too many events queued for " "subscriber %p", s); if (probereq) return 1; /* Drop oldest entry to allow EAP event to be stored. */ e = event_dequeue(s); if (!e) return 1; event_delete(e); } if (s->last_event_failed && probereq && len > 0) { /* * Avoid queuing frames for subscribers that may have left * without unsubscribing. */ wpa_printf(MSG_DEBUG, "WPS UPnP: Do not queue more Probe " "Request frames for subscription %p since last " "delivery failed", s); return -1; } e = os_zalloc(sizeof(*e)); if (e == NULL) return -1; dl_list_init(&e->list); e->s = s; e->data = wpabuf_dup(data); if (e->data == NULL) { os_free(e); return -1; } e->subscriber_sequence = s->next_subscriber_sequence++; if (s->next_subscriber_sequence == 0) s->next_subscriber_sequence++; wpa_printf(MSG_DEBUG, "WPS UPnP: Queue event %p for subscriber %p " "(queue len %u)", e, s, len + 1); dl_list_add_tail(&s->event_queue, &e->list); event_send_all_later(s->sm); return 0; }
/** * event_retry - Called when we had a failure delivering event msg * @e: Event * @do_next_address: skip address e.g. on connect fail */ static void event_retry(struct wps_event_ *e, int do_next_address) { struct subscription *s = e->s; struct upnp_wps_device_sm *sm = s->sm; event_clean(e); /* will set: s->current_event = NULL; */ if (do_next_address) e->retry++; if (e->retry >= s->n_addr) { wpa_printf(MSG_DEBUG, "WPS UPnP: Giving up on sending event " "for %s", e->addr->domain_and_port); return; } event_enqueue_at_begin(s, e); event_send_all_later(sm); }
/* event_send_all_later_handler -- actually send events as needed */ static void event_send_all_later_handler(void *eloop_data, void *user_ctx) { struct upnp_wps_device_sm *sm = user_ctx; struct subscription *s, *tmp; int nerrors = 0; sm->event_send_all_queued = 0; dl_list_for_each_safe(s, tmp, &sm->subscriptions, struct subscription, list) { if (s->current_event == NULL /* not busy */ && !dl_list_empty(&s->event_queue) /* more to do */) { if (event_send_start(s)) nerrors++; } } if (nerrors) { /* Try again later */ event_send_all_later(sm); } }
/* event_send_tx_ready -- actually write event message * * Prequisite: subscription socket descriptor has become ready to * write (because connection to subscriber has been made). * * It is also possible that we are called because the connect has failed; * it is possible to test for this, or we can just go ahead and then * the write will fail. */ static void event_send_tx_ready(int sock, void *eloop_ctx, void *sock_ctx) { struct wps_event_ *e = sock_ctx; struct subscription *s = e->s; struct wpabuf *buf; char *b; assert(e == s->current_event); assert(e->sd == sock); buf = wpabuf_alloc(1000 + wpabuf_len(e->data)); if (buf == NULL) { event_retry(e, 0); goto bad; } wpabuf_printf(buf, "NOTIFY %s HTTP/1.1\r\n", e->addr->path); wpabuf_put_str(buf, "SERVER: Unspecified, UPnP/1.0, Unspecified\r\n"); wpabuf_printf(buf, "HOST: %s\r\n", e->addr->domain_and_port); wpabuf_put_str(buf, "CONTENT-TYPE: text/xml; charset=\"utf-8\"\r\n" "NT: upnp:event\r\n" "NTS: upnp:propchange\r\n"); wpabuf_put_str(buf, "SID: uuid:"); 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, "SEQ: %u\r\n", e->subscriber_sequence); wpabuf_printf(buf, "CONTENT-LENGTH: %d\r\n", (int) wpabuf_len(e->data)); wpabuf_put_str(buf, "\r\n"); /* terminating empty line */ wpabuf_put_buf(buf, e->data); /* Since the message size is pretty small, we should be * able to get the operating system to buffer what we give it * and not have to come back again later to write more... */ #if 0 /* we could: Turn blocking back on? */ fcntl(e->sd, F_SETFL, 0); #endif wpa_printf(MSG_DEBUG, "WPS UPnP: Sending event to %s", e->addr->domain_and_port); if (send_wpabuf(e->sd, buf) < 0) { event_retry(e, 1); goto bad; } wpabuf_free(buf); buf = NULL; if (e->sd_registered) { e->sd_registered = 0; eloop_unregister_sock(e->sd, EVENT_TYPE_WRITE); } /* Set up to read the reply */ e->hread = httpread_create(e->sd, event_got_response_handler, e /* cookie */, 0 /* no data expected */, EVENT_TIMEOUT_SEC); if (e->hread == NULL) { wpa_printf(MSG_ERROR, "WPS UPnP: httpread_create failed"); event_retry(e, 0); goto bad; } return; bad: /* Schedule sending more if there is more to send */ if (s->event_queue) event_send_all_later(s->sm); wpabuf_free(buf); }