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


wpa_supplicant
Author(s): Package maintained by Blaise Gassend
autogenerated on Thu Apr 24 2014 15:34:36