wps_upnp_ssdp.c

Go to the documentation of this file.
00001 
00012 #include "includes.h"
00013 
00014 #include <fcntl.h>
00015 #include <sys/ioctl.h>
00016 #include <net/route.h>
00017 
00018 #include "common.h"
00019 #include "uuid.h"
00020 #include "eloop.h"
00021 #include "wps.h"
00022 #include "wps_upnp.h"
00023 #include "wps_upnp_i.h"
00024 
00025 #define UPNP_CACHE_SEC (UPNP_CACHE_SEC_MIN + 1) /* cache time we use */
00026 #define UPNP_CACHE_SEC_MIN 1800 /* min cachable time per UPnP standard */
00027 #define UPNP_ADVERTISE_REPEAT 2 /* no more than 3 */
00028 #define MAX_MSEARCH 20          /* max simultaneous M-SEARCH replies ongoing */
00029 #define SSDP_TARGET  "239.0.0.0"
00030 #define SSDP_NETMASK "255.0.0.0"
00031 
00032 
00033 /* Check tokens for equality, where tokens consist of letters, digits,
00034  * underscore and hyphen, and are matched case insensitive.
00035  */
00036 static int token_eq(const char *s1, const char *s2)
00037 {
00038         int c1;
00039         int c2;
00040         int end1 = 0;
00041         int end2 = 0;
00042         for (;;) {
00043                 c1 = *s1++;
00044                 c2 = *s2++;
00045                 if (isalpha(c1) && isupper(c1))
00046                         c1 = tolower(c1);
00047                 if (isalpha(c2) && isupper(c2))
00048                         c2 = tolower(c2);
00049                 end1 = !(isalnum(c1) || c1 == '_' || c1 == '-');
00050                 end2 = !(isalnum(c2) || c2 == '_' || c2 == '-');
00051                 if (end1 || end2 || c1 != c2)
00052                         break;
00053         }
00054         return end1 && end2; /* reached end of both words? */
00055 }
00056 
00057 
00058 /* Return length of token (see above for definition of token) */
00059 static int token_length(const char *s)
00060 {
00061         const char *begin = s;
00062         for (;; s++) {
00063                 int c = *s;
00064                 int end = !(isalnum(c) || c == '_' || c == '-');
00065                 if (end)
00066                         break;
00067         }
00068         return s - begin;
00069 }
00070 
00071 
00072 /* return length of interword separation.
00073  * This accepts only spaces/tabs and thus will not traverse a line
00074  * or buffer ending.
00075  */
00076 static int word_separation_length(const char *s)
00077 {
00078         const char *begin = s;
00079         for (;; s++) {
00080                 int c = *s;
00081                 if (c == ' ' || c == '\t')
00082                         continue;
00083                 break;
00084         }
00085         return s - begin;
00086 }
00087 
00088 
00089 /* No. of chars through (including) end of line */
00090 static int line_length(const char *l)
00091 {
00092         const char *lp = l;
00093         while (*lp && *lp != '\n')
00094                 lp++;
00095         if (*lp == '\n')
00096                 lp++;
00097         return lp - l;
00098 }
00099 
00100 
00101 /* No. of chars excluding trailing whitespace */
00102 static int line_length_stripped(const char *l)
00103 {
00104         const char *lp = l + line_length(l);
00105         while (lp > l && !isgraph(lp[-1]))
00106                 lp--;
00107         return lp - l;
00108 }
00109 
00110 
00111 static int str_starts(const char *str, const char *start)
00112 {
00113         return os_strncmp(str, start, os_strlen(start)) == 0;
00114 }
00115 
00116 
00117 /* *************************************************************************
00118  * Advertisements.
00119  * These are multicast to the world to tell them we are here.
00120  * The individual packets are spread out in time to limit loss,
00121  * and then after a much longer period of time the whole sequence
00122  * is repeated again (for NOTIFYs only).
00123  **************************************************************************/
00124 
00134 static struct wpabuf *
00135 next_advertisement(struct advertisement_state_machine *a, int *islast)
00136 {
00137         struct wpabuf *msg;
00138         char *NTString = "";
00139         char uuid_string[80];
00140 
00141         *islast = 0;
00142         uuid_bin2str(a->sm->wps->uuid, uuid_string, sizeof(uuid_string));
00143         msg = wpabuf_alloc(800); /* more than big enough */
00144         if (msg == NULL)
00145                 goto fail;
00146         switch (a->type) {
00147         case ADVERTISE_UP:
00148         case ADVERTISE_DOWN:
00149                 NTString = "NT";
00150                 wpabuf_put_str(msg, "NOTIFY * HTTP/1.1\r\n");
00151                 wpabuf_printf(msg, "HOST: %s:%d\r\n",
00152                               UPNP_MULTICAST_ADDRESS, UPNP_MULTICAST_PORT);
00153                 wpabuf_printf(msg, "CACHE-CONTROL: max-age=%d\r\n",
00154                               UPNP_CACHE_SEC);
00155                 wpabuf_printf(msg, "NTS: %s\r\n",
00156                               (a->type == ADVERTISE_UP ?
00157                                "ssdp:alive" : "ssdp:byebye"));
00158                 break;
00159         case MSEARCH_REPLY:
00160                 NTString = "ST";
00161                 wpabuf_put_str(msg, "HTTP/1.1 200 OK\r\n");
00162                 wpabuf_printf(msg, "CACHE-CONTROL: max-age=%d\r\n",
00163                               UPNP_CACHE_SEC);
00164 
00165                 wpabuf_put_str(msg, "DATE: ");
00166                 format_date(msg);
00167                 wpabuf_put_str(msg, "\r\n");
00168 
00169                 wpabuf_put_str(msg, "EXT:\r\n");
00170                 break;
00171         }
00172 
00173         if (a->type != ADVERTISE_DOWN) {
00174                 /* Where others may get our XML files from */
00175                 wpabuf_printf(msg, "LOCATION: http://%s:%d/%s\r\n",
00176                               a->sm->ip_addr_text, a->sm->web_port,
00177                               UPNP_WPS_DEVICE_XML_FILE);
00178         }
00179 
00180         /* The SERVER line has three comma-separated fields:
00181          *      operating system / version
00182          *      upnp version
00183          *      software package / version
00184          * However, only the UPnP version is really required, the
00185          * others can be place holders... for security reasons
00186          * it is better to NOT provide extra information.
00187          */
00188         wpabuf_put_str(msg, "SERVER: Unspecified, UPnP/1.0, Unspecified\r\n");
00189 
00190         switch (a->state / UPNP_ADVERTISE_REPEAT) {
00191         case 0:
00192                 wpabuf_printf(msg, "%s: upnp:rootdevice\r\n", NTString);
00193                 wpabuf_printf(msg, "USN: uuid:%s::upnp:rootdevice\r\n",
00194                               uuid_string);
00195                 break;
00196         case 1:
00197                 wpabuf_printf(msg, "%s: uuid:%s\r\n", NTString, uuid_string);
00198                 wpabuf_printf(msg, "USN: uuid:%s\r\n", uuid_string);
00199                 break;
00200         case 2:
00201                 wpabuf_printf(msg, "%s: urn:schemas-wifialliance-org:device:"
00202                               "WFADevice:1\r\n", NTString);
00203                 wpabuf_printf(msg, "USN: uuid:%s::urn:schemas-wifialliance-"
00204                               "org:device:WFADevice:1\r\n", uuid_string);
00205                 break;
00206         case 3:
00207                 wpabuf_printf(msg, "%s: urn:schemas-wifialliance-org:service:"
00208                               "WFAWLANConfig:1\r\n", NTString);
00209                 wpabuf_printf(msg, "USN: uuid:%s::urn:schemas-wifialliance-"
00210                               "org:service:WFAWLANConfig:1\r\n", uuid_string);
00211                 break;
00212         }
00213         wpabuf_put_str(msg, "\r\n");
00214 
00215         if (a->state + 1 >= 4 * UPNP_ADVERTISE_REPEAT)
00216                 *islast = 1;
00217 
00218         return msg;
00219 
00220 fail:
00221         wpabuf_free(msg);
00222         return NULL;
00223 }
00224 
00225 
00226 static void advertisement_state_machine_handler(void *eloop_data,
00227                                                 void *user_ctx);
00228 
00229 
00236 void advertisement_state_machine_stop(struct upnp_wps_device_sm *sm,
00237                                       int send_byebye)
00238 {
00239         struct advertisement_state_machine *a = &sm->advertisement;
00240         int islast = 0;
00241         struct wpabuf *msg;
00242         struct sockaddr_in dest;
00243 
00244         eloop_cancel_timeout(advertisement_state_machine_handler, NULL, sm);
00245         if (!send_byebye || sm->multicast_sd < 0)
00246                 return;
00247 
00248         a->type = ADVERTISE_DOWN;
00249         a->state = 0;
00250         a->sm = sm;
00251 
00252         os_memset(&dest, 0, sizeof(dest));
00253         dest.sin_family = AF_INET;
00254         dest.sin_addr.s_addr = inet_addr(UPNP_MULTICAST_ADDRESS);
00255         dest.sin_port = htons(UPNP_MULTICAST_PORT);
00256 
00257         while (!islast) {
00258                 msg = next_advertisement(a, &islast);
00259                 if (msg == NULL)
00260                         break;
00261                 if (sendto(sm->multicast_sd, wpabuf_head(msg), wpabuf_len(msg),
00262                            0, (struct sockaddr *) &dest, sizeof(dest)) < 0) {
00263                         wpa_printf(MSG_INFO, "WPS UPnP: Advertisement sendto "
00264                                    "failed: %d (%s)", errno, strerror(errno));
00265                 }
00266                 wpabuf_free(msg);
00267                 a->state++;
00268         }
00269 }
00270 
00271 
00272 static void advertisement_state_machine_handler(void *eloop_data,
00273                                                 void *user_ctx)
00274 {
00275         struct upnp_wps_device_sm *sm = user_ctx;
00276         struct advertisement_state_machine *a = &sm->advertisement;
00277         struct wpabuf *msg;
00278         int next_timeout_msec = 100;
00279         int next_timeout_sec = 0;
00280         struct sockaddr_in dest;
00281         int islast = 0;
00282 
00283         /*
00284          * Each is sent twice (in case lost) w/ 100 msec delay between;
00285          * spec says no more than 3 times.
00286          * One pair for rootdevice, one pair for uuid, and a pair each for
00287          * each of the two urns.
00288          * The entire sequence must be repeated before cache control timeout
00289          * (which  is min  1800 seconds),
00290          * recommend random portion of half of the advertised cache control age
00291          * to ensure against loss... perhaps 1800/4 + rand*1800/4 ?
00292          * Delay random interval < 100 msec prior to initial sending.
00293          * TTL of 4
00294          */
00295 
00296         wpa_printf(MSG_MSGDUMP, "WPS UPnP: Advertisement state=%d", a->state);
00297         msg = next_advertisement(a, &islast);
00298         if (msg == NULL)
00299                 return;
00300 
00301         os_memset(&dest, 0, sizeof(dest));
00302         dest.sin_family = AF_INET;
00303         dest.sin_addr.s_addr = inet_addr(UPNP_MULTICAST_ADDRESS);
00304         dest.sin_port = htons(UPNP_MULTICAST_PORT);
00305 
00306         if (sendto(sm->multicast_sd, wpabuf_head(msg), wpabuf_len(msg), 0,
00307                    (struct sockaddr *) &dest, sizeof(dest)) == -1) {
00308                 wpa_printf(MSG_ERROR, "WPS UPnP: Advertisement sendto failed:"
00309                            "%d (%s)", errno, strerror(errno));
00310                 next_timeout_msec = 0;
00311                 next_timeout_sec = 10; /* ... later */
00312         } else if (islast) {
00313                 a->state = 0; /* wrap around */
00314                 if (a->type == ADVERTISE_DOWN) {
00315                         wpa_printf(MSG_DEBUG, "WPS UPnP: ADVERTISE_DOWN->UP");
00316                         a->type = ADVERTISE_UP;
00317                         /* do it all over again right away */
00318                 } else {
00319                         u16 r;
00320                         /*
00321                          * Start over again after a long timeout
00322                          * (see notes above)
00323                          */
00324                         next_timeout_msec = 0;
00325                         os_get_random((void *) &r, sizeof(r));
00326                         next_timeout_sec = UPNP_CACHE_SEC / 4 +
00327                                 (((UPNP_CACHE_SEC / 4) * r) >> 16);
00328                         sm->advertise_count++;
00329                         wpa_printf(MSG_DEBUG, "WPS UPnP: ADVERTISE_UP (#%u); "
00330                                    "next in %d sec",
00331                                    sm->advertise_count, next_timeout_sec);
00332                 }
00333         } else {
00334                 a->state++;
00335         }
00336 
00337         wpabuf_free(msg);
00338 
00339         eloop_register_timeout(next_timeout_sec, next_timeout_msec,
00340                                advertisement_state_machine_handler, NULL, sm);
00341 }
00342 
00343 
00350 int advertisement_state_machine_start(struct upnp_wps_device_sm *sm)
00351 {
00352         struct advertisement_state_machine *a = &sm->advertisement;
00353         int next_timeout_msec;
00354 
00355         advertisement_state_machine_stop(sm, 0);
00356 
00357         /*
00358          * Start out advertising down, this automatically switches
00359          * to advertising up which signals our restart.
00360          */
00361         a->type = ADVERTISE_DOWN;
00362         a->state = 0;
00363         a->sm = sm;
00364         /* (other fields not used here) */
00365 
00366         /* First timeout should be random interval < 100 msec */
00367         next_timeout_msec = (100 * (os_random() & 0xFF)) >> 8;
00368         return eloop_register_timeout(0, next_timeout_msec,
00369                                       advertisement_state_machine_handler,
00370                                       NULL, sm);
00371 }
00372 
00373 
00374 /* *************************************************************************
00375  * M-SEARCH replies
00376  * These are very similar to the multicast advertisements, with some
00377  * small changes in data content; and they are sent (UDP) to a specific
00378  * unicast address instead of multicast.
00379  * They are sent in response to a UDP M-SEARCH packet.
00380  **************************************************************************/
00381 
00382 static void msearchreply_state_machine_handler(void *eloop_data,
00383                                                void *user_ctx);
00384 
00385 
00391 void msearchreply_state_machine_stop(struct advertisement_state_machine *a)
00392 {
00393         struct upnp_wps_device_sm *sm = a->sm;
00394         wpa_printf(MSG_DEBUG, "WPS UPnP: M-SEARCH stop");
00395         if (a->next == a) {
00396                 sm->msearch_replies = NULL;
00397         } else {
00398                 if (sm->msearch_replies == a)
00399                         sm->msearch_replies = a->next;
00400                 a->next->prev = a->prev;
00401                 a->prev->next = a->next;
00402         }
00403         os_free(a);
00404         sm->n_msearch_replies--;
00405 }
00406 
00407 
00408 static void msearchreply_state_machine_handler(void *eloop_data,
00409                                                void *user_ctx)
00410 {
00411         struct advertisement_state_machine *a = user_ctx;
00412         struct upnp_wps_device_sm *sm = a->sm;
00413         struct wpabuf *msg;
00414         int next_timeout_msec = 100;
00415         int next_timeout_sec = 0;
00416         int islast = 0;
00417 
00418         /*
00419          * Each response is sent twice (in case lost) w/ 100 msec delay
00420          * between; spec says no more than 3 times.
00421          * One pair for rootdevice, one pair for uuid, and a pair each for
00422          * each of the two urns.
00423          */
00424 
00425         /* TODO: should only send the requested response types */
00426 
00427         wpa_printf(MSG_MSGDUMP, "WPS UPnP: M-SEARCH reply state=%d (%s:%d)",
00428                    a->state, inet_ntoa(a->client.sin_addr),
00429                    ntohs(a->client.sin_port));
00430         msg = next_advertisement(a, &islast);
00431         if (msg == NULL)
00432                 return;
00433 
00434         /*
00435          * Send it on the multicast socket to avoid having to set up another
00436          * socket.
00437          */
00438         if (sendto(sm->multicast_sd, wpabuf_head(msg), wpabuf_len(msg), 0,
00439                    (struct sockaddr *) &a->client, sizeof(a->client)) < 0) {
00440                 wpa_printf(MSG_DEBUG, "WPS UPnP: M-SEARCH reply sendto "
00441                            "errno %d (%s) for %s:%d",
00442                            errno, strerror(errno),
00443                            inet_ntoa(a->client.sin_addr),
00444                            ntohs(a->client.sin_port));
00445                 /* Ignore error and hope for the best */
00446         }
00447         wpabuf_free(msg);
00448         if (islast) {
00449                 wpa_printf(MSG_DEBUG, "WPS UPnP: M-SEARCH reply done");
00450                 msearchreply_state_machine_stop(a);
00451                 return;
00452         }
00453         a->state++;
00454 
00455         wpa_printf(MSG_MSGDUMP, "WPS UPnP: M-SEARCH reply in %d.%03d sec",
00456                    next_timeout_sec, next_timeout_msec);
00457         eloop_register_timeout(next_timeout_sec, next_timeout_msec,
00458                                msearchreply_state_machine_handler, sm, a);
00459 }
00460 
00461 
00478 static void msearchreply_state_machine_start(struct upnp_wps_device_sm *sm,
00479                                              struct sockaddr_in *client,
00480                                              int mx)
00481 {
00482         struct advertisement_state_machine *a;
00483         int next_timeout_sec;
00484         int next_timeout_msec;
00485 
00486         wpa_printf(MSG_DEBUG, "WPS UPnP: M-SEARCH reply start (%d "
00487                    "outstanding)", sm->n_msearch_replies);
00488         if (sm->n_msearch_replies >= MAX_MSEARCH) {
00489                 wpa_printf(MSG_INFO, "WPS UPnP: Too many outstanding "
00490                            "M-SEARCH replies");
00491                 return;
00492         }
00493 
00494         a = os_zalloc(sizeof(*a));
00495         if (a == NULL)
00496                 return;
00497         a->type = MSEARCH_REPLY;
00498         a->state = 0;
00499         a->sm = sm;
00500         os_memcpy(&a->client, client, sizeof(*client));
00501         /* Wait time depending on MX value */
00502         next_timeout_msec = (1000 * mx * (os_random() & 0xFF)) >> 8;
00503         next_timeout_sec = next_timeout_msec / 1000;
00504         next_timeout_msec = next_timeout_msec % 1000;
00505         if (eloop_register_timeout(next_timeout_sec, next_timeout_msec,
00506                                    msearchreply_state_machine_handler, sm,
00507                                    a)) {
00508                 /* No way to recover (from malloc failure) */
00509                 goto fail;
00510         }
00511         /* Remember for future cleanup */
00512         if (sm->msearch_replies) {
00513                 a->next = sm->msearch_replies;
00514                 a->prev = a->next->prev;
00515                 a->prev->next = a;
00516                 a->next->prev = a;
00517         } else {
00518                 sm->msearch_replies = a->next = a->prev = a;
00519         }
00520         sm->n_msearch_replies++;
00521         return;
00522 
00523 fail:
00524         wpa_printf(MSG_INFO, "WPS UPnP: M-SEARCH reply failure!");
00525         eloop_cancel_timeout(msearchreply_state_machine_handler, sm, a);
00526         os_free(a);
00527 }
00528 
00529 
00552 static void ssdp_parse_msearch(struct upnp_wps_device_sm *sm,
00553                                struct sockaddr_in *client, const char *data)
00554 {
00555         const char *start = data;
00556         const char *end;
00557         int got_host = 0;
00558         int got_st = 0, st_match = 0;
00559         int got_man = 0;
00560         int got_mx = 0;
00561         int mx = 0;
00562 
00563         /*
00564          * Skip first line M-SEARCH * HTTP/1.1
00565          * (perhaps we should check remainder of the line for syntax)
00566          */
00567         data += line_length(data);
00568 
00569         /* Parse remaining lines */
00570         for (; *data != '\0'; data += line_length(data)) {
00571                 end = data + line_length_stripped(data);
00572                 if (token_eq(data, "host")) {
00573                         /* The host line indicates who the packet
00574                          * is addressed to... but do we really care?
00575                          * Note that Microsoft sometimes does funny
00576                          * stuff with the HOST: line.
00577                          */
00578 #if 0   /* could be */
00579                         data += token_length(data);
00580                         data += word_separation_length(data);
00581                         if (*data != ':')
00582                                 goto bad;
00583                         data++;
00584                         data += word_separation_length(data);
00585                         /* UPNP_MULTICAST_ADDRESS */
00586                         if (!str_starts(data, "239.255.255.250"))
00587                                 goto bad;
00588                         data += os_strlen("239.255.255.250");
00589                         if (*data == ':') {
00590                                 if (!str_starts(data, ":1900"))
00591                                         goto bad;
00592                         }
00593 #endif  /* could be */
00594                         got_host = 1;
00595                         continue;
00596                 } else if (token_eq(data, "st")) {
00597                         /* There are a number of forms; we look
00598                          * for one that matches our case.
00599                          */
00600                         got_st = 1;
00601                         data += token_length(data);
00602                         data += word_separation_length(data);
00603                         if (*data != ':')
00604                                 continue;
00605                         data++;
00606                         data += word_separation_length(data);
00607                         if (str_starts(data, "ssdp:all")) {
00608                                 st_match = 1;
00609                                 continue;
00610                         }
00611                         if (str_starts(data, "upnp:rootdevice")) {
00612                                 st_match = 1;
00613                                 continue;
00614                         }
00615                         if (str_starts(data, "uuid:")) {
00616                                 char uuid_string[80];
00617                                 data += os_strlen("uuid:");
00618                                 uuid_bin2str(sm->wps->uuid, uuid_string,
00619                                              sizeof(uuid_string));
00620                                 if (str_starts(data, uuid_string))
00621                                         st_match = 1;
00622                                 continue;
00623                         }
00624 #if 0
00625                         /* FIX: should we really reply to IGD string? */
00626                         if (str_starts(data, "urn:schemas-upnp-org:device:"
00627                                        "InternetGatewayDevice:1")) {
00628                                 st_match = 1;
00629                                 continue;
00630                         }
00631 #endif
00632                         if (str_starts(data, "urn:schemas-wifialliance-org:"
00633                                        "service:WFAWLANConfig:1")) {
00634                                 st_match = 1;
00635                                 continue;
00636                         }
00637                         if (str_starts(data, "urn:schemas-wifialliance-org:"
00638                                        "device:WFADevice:1")) {
00639                                 st_match = 1;
00640                                 continue;
00641                         }
00642                         continue;
00643                 } else if (token_eq(data, "man")) {
00644                         data += token_length(data);
00645                         data += word_separation_length(data);
00646                         if (*data != ':')
00647                                 continue;
00648                         data++;
00649                         data += word_separation_length(data);
00650                         if (!str_starts(data, "\"ssdp:discover\"")) {
00651                                 wpa_printf(MSG_DEBUG, "WPS UPnP: Unexpected "
00652                                            "M-SEARCH man-field");
00653                                 goto bad;
00654                         }
00655                         got_man = 1;
00656                         continue;
00657                 } else if (token_eq(data, "mx")) {
00658                         data += token_length(data);
00659                         data += word_separation_length(data);
00660                         if (*data != ':')
00661                                 continue;
00662                         data++;
00663                         data += word_separation_length(data);
00664                         mx = atol(data);
00665                         got_mx = 1;
00666                         continue;
00667                 }
00668                 /* ignore anything else */
00669         }
00670         if (!got_host || !got_st || !got_man || !got_mx || mx < 0) {
00671                 wpa_printf(MSG_DEBUG, "WPS UPnP: Invalid M-SEARCH: %d %d %d "
00672                            "%d mx=%d", got_host, got_st, got_man, got_mx, mx);
00673                 goto bad;
00674         }
00675         if (!st_match) {
00676                 wpa_printf(MSG_DEBUG, "WPS UPnP: Ignored M-SEARCH (no ST "
00677                            "match)");
00678                 return;
00679         }
00680         if (mx > 120)
00681                 mx = 120; /* UPnP-arch-DeviceArchitecture, 1.2.3 */
00682         msearchreply_state_machine_start(sm, client, mx);
00683         return;
00684 
00685 bad:
00686         wpa_printf(MSG_INFO, "WPS UPnP: Failed to parse M-SEARCH");
00687         wpa_printf(MSG_MSGDUMP, "WPS UPnP: M-SEARCH data:\n%s", start);
00688 }
00689 
00690 
00691 /* Listening for (UDP) discovery (M-SEARCH) packets */
00692 
00701 void ssdp_listener_stop(struct upnp_wps_device_sm *sm)
00702 {
00703         if (sm->ssdp_sd_registered) {
00704                 eloop_unregister_sock(sm->ssdp_sd, EVENT_TYPE_READ);
00705                 sm->ssdp_sd_registered = 0;
00706         }
00707 
00708         if (sm->ssdp_sd != -1) {
00709                 close(sm->ssdp_sd);
00710                 sm->ssdp_sd = -1;
00711         }
00712 
00713         eloop_cancel_timeout(msearchreply_state_machine_handler, sm,
00714                              ELOOP_ALL_CTX);
00715 }
00716 
00717 
00718 static void ssdp_listener_handler(int sd, void *eloop_ctx, void *sock_ctx)
00719 {
00720         struct upnp_wps_device_sm *sm = sock_ctx;
00721         struct sockaddr_in addr; /* client address */
00722         socklen_t addr_len;
00723         int nread;
00724         char buf[MULTICAST_MAX_READ], *pos;
00725 
00726         addr_len = sizeof(addr);
00727         nread = recvfrom(sm->ssdp_sd, buf, sizeof(buf) - 1, 0,
00728                          (struct sockaddr *) &addr, &addr_len);
00729         if (nread <= 0)
00730                 return;
00731         buf[nread] = '\0'; /* need null termination for algorithm */
00732 
00733         if (str_starts(buf, "NOTIFY ")) {
00734                 /*
00735                  * Silently ignore NOTIFYs to avoid filling debug log with
00736                  * unwanted messages.
00737                  */
00738                 return;
00739         }
00740 
00741         pos = os_strchr(buf, '\n');
00742         if (pos)
00743                 *pos = '\0';
00744         wpa_printf(MSG_MSGDUMP, "WPS UPnP: Received SSDP packet from %s:%d: "
00745                    "%s", inet_ntoa(addr.sin_addr), ntohs(addr.sin_port), buf);
00746         if (pos)
00747                 *pos = '\n';
00748 
00749         /* Parse first line */
00750         if (os_strncasecmp(buf, "M-SEARCH", os_strlen("M-SEARCH")) == 0 &&
00751             !isgraph(buf[strlen("M-SEARCH")])) {
00752                 ssdp_parse_msearch(sm, &addr, buf);
00753                 return;
00754         }
00755 
00756         /* Ignore anything else */
00757 }
00758 
00759 
00760 int ssdp_listener_open(void)
00761 {
00762         struct sockaddr_in addr;
00763         struct ip_mreq mcast_addr;
00764         int on = 1;
00765         /* per UPnP spec, keep IP packet time to live (TTL) small */
00766         unsigned char ttl = 4;
00767         int sd;
00768 
00769         sd = socket(AF_INET, SOCK_DGRAM, 0);
00770         if (sd < 0)
00771                 goto fail;
00772         if (fcntl(sd, F_SETFL, O_NONBLOCK) != 0)
00773                 goto fail;
00774         if (setsockopt(sd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)))
00775                 goto fail;
00776         os_memset(&addr, 0, sizeof(addr));
00777         addr.sin_family = AF_INET;
00778         addr.sin_addr.s_addr = htonl(INADDR_ANY);
00779         addr.sin_port = htons(UPNP_MULTICAST_PORT);
00780         if (bind(sd, (struct sockaddr *) &addr, sizeof(addr)))
00781                 goto fail;
00782         os_memset(&mcast_addr, 0, sizeof(mcast_addr));
00783         mcast_addr.imr_interface.s_addr = htonl(INADDR_ANY);
00784         mcast_addr.imr_multiaddr.s_addr = inet_addr(UPNP_MULTICAST_ADDRESS);
00785         if (setsockopt(sd, IPPROTO_IP, IP_ADD_MEMBERSHIP,
00786                        (char *) &mcast_addr, sizeof(mcast_addr)))
00787                 goto fail;
00788         if (setsockopt(sd, IPPROTO_IP, IP_MULTICAST_TTL,
00789                        &ttl, sizeof(ttl)))
00790                 goto fail;
00791 
00792         return sd;
00793 
00794 fail:
00795         if (sd >= 0)
00796                 close(sd);
00797         return -1;
00798 }
00799 
00800 
00809 int ssdp_listener_start(struct upnp_wps_device_sm *sm)
00810 {
00811         sm->ssdp_sd = ssdp_listener_open();
00812 
00813         if (eloop_register_sock(sm->ssdp_sd, EVENT_TYPE_READ,
00814                                 ssdp_listener_handler, NULL, sm))
00815                 goto fail;
00816         sm->ssdp_sd_registered = 1;
00817         return 0;
00818 
00819 fail:
00820         /* Error */
00821         wpa_printf(MSG_ERROR, "WPS UPnP: ssdp_listener_start failed");
00822         ssdp_listener_stop(sm);
00823         return -1;
00824 }
00825 
00826 
00839 int add_ssdp_network(const char *net_if)
00840 {
00841 #ifdef __linux__
00842         int ret = -1;
00843         int sock = -1;
00844         struct rtentry rt;
00845         struct sockaddr_in *sin;
00846 
00847         if (!net_if)
00848                 goto fail;
00849 
00850         os_memset(&rt, 0, sizeof(rt));
00851         sock = socket(AF_INET, SOCK_DGRAM, 0);
00852         if (sock < 0)
00853                 goto fail;
00854 
00855         rt.rt_dev = (char *) net_if;
00856         sin = aliasing_hide_typecast(&rt.rt_dst, struct sockaddr_in);
00857         sin->sin_family = AF_INET;
00858         sin->sin_port = 0;
00859         sin->sin_addr.s_addr = inet_addr(SSDP_TARGET);
00860         sin = aliasing_hide_typecast(&rt.rt_genmask, struct sockaddr_in);
00861         sin->sin_family = AF_INET;
00862         sin->sin_port = 0;
00863         sin->sin_addr.s_addr = inet_addr(SSDP_NETMASK);
00864         rt.rt_flags = RTF_UP;
00865         if (ioctl(sock, SIOCADDRT, &rt) < 0) {
00866                 if (errno == EPERM) {
00867                         wpa_printf(MSG_DEBUG, "add_ssdp_network: No "
00868                                    "permissions to add routing table entry");
00869                         /* Continue to allow testing as non-root */
00870                 } else if (errno != EEXIST) {
00871                         wpa_printf(MSG_INFO, "add_ssdp_network() ioctl errno "
00872                                    "%d (%s)", errno, strerror(errno));
00873                         goto fail;
00874                 }
00875         }
00876 
00877         ret = 0;
00878 
00879 fail:
00880         if (sock >= 0)
00881                 close(sock);
00882 
00883         return ret;
00884 #else /* __linux__ */
00885         return 0;
00886 #endif /* __linux__ */
00887 }
00888 
00889 
00890 int ssdp_open_multicast_sock(u32 ip_addr)
00891 {
00892         int sd;
00893          /* per UPnP-arch-DeviceArchitecture, 1. Discovery, keep IP packet
00894           * time to live (TTL) small */
00895         unsigned char ttl = 4;
00896 
00897         sd = socket(AF_INET, SOCK_DGRAM, 0);
00898         if (sd < 0)
00899                 return -1;
00900 
00901 #if 0   /* maybe ok if we sometimes block on writes */
00902         if (fcntl(sd, F_SETFL, O_NONBLOCK) != 0)
00903                 return -1;
00904 #endif
00905 
00906         if (setsockopt(sd, IPPROTO_IP, IP_MULTICAST_IF,
00907                        &ip_addr, sizeof(ip_addr)))
00908                 return -1;
00909         if (setsockopt(sd, IPPROTO_IP, IP_MULTICAST_TTL,
00910                        &ttl, sizeof(ttl)))
00911                 return -1;
00912 
00913 #if 0   /* not needed, because we don't receive using multicast_sd */
00914         {
00915                 struct ip_mreq mreq;
00916                 mreq.imr_multiaddr.s_addr = inet_addr(UPNP_MULTICAST_ADDRESS);
00917                 mreq.imr_interface.s_addr = ip_addr;
00918                 wpa_printf(MSG_DEBUG, "WPS UPnP: Multicast addr 0x%x if addr "
00919                            "0x%x",
00920                            mreq.imr_multiaddr.s_addr,
00921                            mreq.imr_interface.s_addr);
00922                 if (setsockopt(sd, IPPROTO_IP, IP_ADD_MEMBERSHIP, &mreq,
00923                                 sizeof(mreq))) {
00924                         wpa_printf(MSG_ERROR,
00925                                    "WPS UPnP: setsockopt "
00926                                    "IP_ADD_MEMBERSHIP errno %d (%s)",
00927                                    errno, strerror(errno));
00928                         return -1;
00929                 }
00930         }
00931 #endif  /* not needed */
00932 
00933         /*
00934          * TODO: What about IP_MULTICAST_LOOP? It seems to be on by default?
00935          * which aids debugging I suppose but isn't really necessary?
00936          */
00937 
00938         return sd;
00939 }
00940 
00941 
00948 int ssdp_open_multicast(struct upnp_wps_device_sm *sm)
00949 {
00950         sm->multicast_sd = ssdp_open_multicast_sock(sm->ip_addr);
00951         if (sm->multicast_sd < 0)
00952                 return -1;
00953         return 0;
00954 }
00955 
 All Data Structures Files Functions Variables Typedefs Enumerations Enumerator Defines

Generated on Sat Nov 21 23:16:55 2009 for hostapd by  doxygen 1.6.1