/* * UPnP portable main loop. * It initializes the UPnP protocol and event variables. * And loop handler the UPnP incoming requests. */ void upnp_mainloop() { UPNP_CONTEXT *context = &upnp_context; /* initialize upnp */ upnp_init(context); /* main loop */ while (1) { switch (upnp_flag) { case UPNP_FLAG_SHUTDOWN: upnp_shutdown(context); upnp_syslog(LOG_INFO, "UPnP shutdown!"); return; case UPNP_FLAG_RESTART: upnp_shutdown(context); upnp_init(context); upnp_syslog(LOG_INFO, "UPnP restart!"); break; case 0: default: upnp_dispatch(context); break; } } return; }
/* Do UPnP interface initialization */ int upnp_ifattach(UPNP_CONTEXT *context, char *ifname, UPNP_DEVICE *device) { UPNP_INTERFACE *ifp; /* Allocate interface space */ ifp = (UPNP_INTERFACE *)malloc(sizeof(*ifp)); if (ifp == 0) return 0; memset(ifp, 0, sizeof(*ifp)); /* Setup context */ strncpy(ifp->ifname, ifname, sizeof(ifp->ifname)); ifp->ifname[sizeof(ifp->ifname)-1] = '\0'; ifp->http_sock = -1; if (get_if_ipaddr(ifp) != 0) { free(ifp); return 0; } /* Do prepend */ ifp->next = context->iflist; context->iflist = ifp; /* Attache device */ if (context->ssdp_sock == -1) { if (ssdp_init(context) != 0) { free(ifp); return -1; } } context->focus_ifp = ifp; /* Perform per interface protocol initialization */ if (upnp_http_init(context) != 0) { upnp_syslog(LOG_ERR, "upnp_http_init::%s init error!", ifp->ifname); return -1; } if (ssdp_add_multi(context) == -1) { upnp_syslog(LOG_ERR, "ssdp_add_multi::%s error!", ifp->ifname); return -1; } /* * Hook device table to each interface. * The init function of each device * intialize the event variables, and send SSDP ALIVE to each * interface */ if (upnp_device_attach(context, device) == -1) { upnp_syslog(LOG_ERR, "upnp_device_attach::%s(%s) error!", device->root_device_xml, ifp->ifname); return -1; } return 0; }
/* Shutdown all the UPnP interfaces */ void upnp_deinit(UPNP_CONTEXT *context) { UPNP_INTERFACE *ifp; while ((ifp = context->iflist) != 0) { context->focus_ifp = ifp; /* Unhook device database */ upnp_device_detach(context, ifp->device); /* shutdown interface protocol */ upnp_http_shutdown(context); ssdp_del_multi(context); /* Drop this link and free */ context->iflist = ifp->next; free(ifp); } /* Shutdown the mutlicast receive socket */ ssdp_shutdown(context); upnp_syslog(LOG_INFO, "UPnP daemon stopped"); free(context); return; }
/* Read data from socket until end of header of error occurs */ static int read_header(UPNP_CONTEXT *context, struct upnp_method *methods) { int buf_size; int n; while (get_msghdr(context)) { /* check buffer size */ buf_size = sizeof(context->buf) - context->end; if (buf_size <= 0) { upnp_syslog(LOG_ERR, "Cannot get enough buffer"); context->status = R_BAD_REQUEST; break; } /* read socket into the buffer */ n = read_sock(context->fd, &context->buf[context->end], buf_size, 0); if (n <= 0) { context->status = R_ERROR; return -2; } /* Move end */ context->end += n; } if (context->status != 0) return -1; context->content = &context->buf[context->index]; context->content_len = context->end - context->index; return 0; }
/* Parse the URI */ int parse_uri(UPNP_CONTEXT *context, struct upnp_method *methods) { char *p = context->url; int pos; /* skip white spaces */ while (*p == ' ' || *p == '\t') p++; pos = strcspn(p, "\t "); context->url = p; context->url[pos] = 0; /* SSDP only accepts "*" */ if (context->method == METHOD_MSEARCH && strcmp(context->url, "*") != 0) { upnp_syslog(LOG_ERR, "SSDP method=%d, URL=%s", context->method, context->url); return -1; } /* skip URL and white spaces */ p += pos+1; while (*p == ' ' || *p == '\t') p++; /* also check HTTP version */ if (memcmp(p, "HTTP/1.0", 8) != 0 && memcmp(p, "HTTP/1.1", 8) != 0) { return -1; } return 0; }
void upnp_device_detach(UPNP_CONTEXT *context, UPNP_DEVICE *device) { UPNP_INTERFACE *ifp = context->focus_ifp; upnp_syslog(LOG_INFO, "%s: detach %s", ifp->ifname, device->root_device_xml); if (!ifp->device || ifp->device != device) return; /* Do device specific stop function */ if (device->close) (*device->close)(context); /* Clear event variables and subscribers */ gena_shutdown(context); /* Send byebye */ ssdp_byebye(context); /* Free per interface advertise list */ upnp_device_advlist_deinit(context); /* detach it */ ifp->device = 0; return; }
int upnp_device_attach(UPNP_CONTEXT *context, UPNP_DEVICE *device) { UPNP_INTERFACE *ifp = context->focus_ifp; upnp_syslog(LOG_INFO, "%s: attach %s", ifp->ifname, device->root_device_xml); /* Check if the device has already attached? */ if (ifp->device && ifp->device == device) return 0; ifp->device = device; /* Setup per interface UUIDs */ upnp_device_advlist_init(context); /* Remove this chain, if open error */ if (device->open && (*device->open)(context) != 0) { ifp->device = 0; return -1; } /* Initialize gena event variable */ gena_init(context); /* Send alive here */ ssdp_alive(context); return 0; }
int upnp_msg_save(UPNP_CONTEXT *context, char *name, char *value) { UPNP_MSG *msg; /* FIXEME: Duplicate name, value to other buffer * seems safer. */ /* Find in list */ for (msg = context->msglist; msg; msg = msg->next) { if (strcmp(name, msg->name) == 0) { msg->value = value; return 0; } } /* Create a new one */ msg = (UPNP_MSG *)calloc(1, sizeof(*msg)); if (msg == NULL) { upnp_syslog(LOG_ERR, "Out of memory!"); return -1; } msg->name = name; msg->value = value; /* Do prepend */ msg->next = context->msglist; context->msglist = msg; return 0; }
int upnp_device_attach(UPNP_CONTEXT *context, UPNP_DEVICE *device) { UPNP_INTERFACE *ifp = context->focus_ifp; UPNP_DEVCHAIN *chain; upnp_syslog(LOG_INFO, "%s: attach %s", ifp->ifname, device->root_device_xml); /* Check if the device has already attached? */ for (chain = ifp->device_chain; chain; chain = chain->next) { /* Attached, do nothing */ if (chain->device == device) return 0; } /* Allocate a new one */ chain = (UPNP_DEVCHAIN *)malloc(sizeof(*chain)); if (chain == 0) return -1; chain->ifp = ifp; chain->device = device; /* Prepend this chain */ chain->next = ifp->device_chain; ifp->device_chain = chain; ifp->focus_devchain = chain; /* Remove this chain, if open error */ if ((*device->open)(context) != 0) { ifp->device_chain = chain->next; ifp->focus_devchain = chain->next; free(chain); return -1; } /* Initialize gena event variable */ gena_init(context); /* Send byby here */ ssdp_byebye(context); upnp_sleep(1); /* Send alive here */ ssdp_alive(context); return 0; }
void upnp_device_detach(UPNP_CONTEXT *context, UPNP_DEVICE *device) { UPNP_INTERFACE *ifp = context->focus_ifp; UPNP_DEVCHAIN *chain, *prev; upnp_syslog(LOG_INFO, "%s: detach %s", ifp->ifname, device->root_device_xml); /* Locate the device chain */ for (prev = 0, chain = ifp->device_chain; chain; prev = chain, chain = chain->next) { if (chain->device == device) break; } if (chain == 0) return; ifp->focus_devchain = chain; /* Do device specific stop function */ (*device->close)(context); /* Clear event variables and subscribers */ gena_shutdown(context); /* Send byebye */ ssdp_byebye(context); /* detach it */ if (prev == 0) ifp->device_chain = chain->next; else prev->next = chain->next; /* Free this chain */ free(chain); return; }
/* Disassemble header and record the message headers */ static int record_msghdr(UPNP_CONTEXT *context, char *ptr, int eol, int eoh) { int i; /* clear end of line characters */ for (i = 0; i < eol; i++) context->buf[context->index-i-1] = 0; /* clear end of header characters */ for (i = 0; i < eoh; i++) context->buf[context->index+i] = 0; if (context->header_num == MAX_HEADERS) { upnp_syslog(LOG_ERR, "Too many header to handle!"); return -1; } context->msghdrs[context->header_num] = ptr; context->header_num++; return 0; }
/* Parse HTTP header and set appropriate env variables */ int parse_msghdr(UPNP_CONTEXT *context, struct upnp_method *methods) { char *token, *value; char *p; int i; /* skip the first line (method, url, and HTTP version), which we already processed */ for (i = 1; i < context->header_num; i++) { token = context->msghdrs[i]; strtok_r(token, ":", &value); /* skip white spaces */ while (*token == '\t' || *token == ' ') token++; if (value != NULL) { /* skip white spaces */ while (*value == '\t' || *value == ' ') value++; /* capitalize token */ for (p = token; *p; p++) *p = toupper(*p); save_header(context, token, value); } else { upnp_syslog(LOG_ERR, "Null value in parse_msghdr()"); goto error_out; } } return 0; error_out: context->status = R_ERROR; return -1; }
/* Process the UPnP request message */ void upnp_request_handler(UPNP_CONTEXT *context) { int len; UPNP_REQUEST *request; UPNP_DEVICE *device = 0; UPNP_INTERFACE *ifp = context->focus_ifp; UPNP_DEVCHAIN *chain; /* Read message */ len = read_request(context); if (len <= 0) return; request = (UPNP_REQUEST *)context->buf; if (get_device_from_reqcmd(request->cmd)) { int i; for (i = 0; (device = upnp_device_table[i]) != 0; i++) { if (strcmp(device->root_device_xml, request->url) == 0) break; } if (device == 0) return; } /* Switch the operation */ switch (request->cmd) { case UPNP_REQ_DEV_ADD: upnp_syslog(LOG_INFO, "UPNP_CMD_DEV_ADD"); upnp_device_attach(context, device); break; case UPNP_REQ_DEV_DEL: upnp_syslog(LOG_INFO, "UPNP_CMD_DEV_DEL"); upnp_device_detach(context, device); break; case UPNP_REQ_GET_STATE_VAR: request->status = soap_get_state_var( context, request->url, request->var[0].statevar, &request->var[0].value); upnp_request_response(context, request); break; case UPNP_REQ_SET_EVENT_VAR: gena_event_alarm(context, request->url, request->num, request->var); break; case UPNP_REQ_GENA_NOTIFY: gena_event_alarm(context, request->url, 0, 0); break; default: upnp_syslog(LOG_INFO, "Device command."); for (chain = ifp->device_chain; chain; chain = chain->next) { if (chain->device == device) { ifp->focus_devchain = chain; device->request(context, request); break; } } break; } return; }
/* Give a unique uuid to this rootdevice */ int upnp_device_renew_uuid(UPNP_CONTEXT *context, UPNP_DEVICE *device) { char *s, *p; char deviceType[256]; char serviceType[256]; char new_uuid[64]; UPNP_DESCRIPTION *desc; /* Find root device */ for (desc = device->description_table; desc; desc++) { if (strcmp(device->root_device_xml, desc->name+1) == 0) break; } if (desc == 0) { upnp_syslog(LOG_ERR, "Root device can not find it's xml."); return -1; } /* * Update UUID of the root device xml * for each AP/router. By using the MAC address * and the value between <deviceType>...</deviceType>, we can * generate a unique UUID. */ for (p = desc->xml; *p != 0; p++) { /* Search for <deviceType> */ if (strncmp(p, DEVICE_BTAG, strlen(DEVICE_BTAG)) == 0) { p += strlen(DEVICE_BTAG); /* Find the balanced </deviceType> */ s = strstr(p, DEVICE_ETAG); if (s == 0 || (s-p) > sizeof(deviceType)-1) { upnp_syslog(LOG_ERR, "Parse format:<deviceType>...</deviceType> error.\n"); return -1; } /* Save deviceType */ memcpy(deviceType, p, s-p); deviceType[s-p] = '\0'; p = s + strlen(DEVICE_ETAG); } /* * For example, * <deviceType>urn:schemas-upnp-org:device:InternetGatewayDevice:1</deviceType> * the value between <UDN>...</UDN> is the UUID we have to replace. * The UUID format is such as, uuid:eb9ab5b2-981c-4401-a20e-b7bcde359dbb. * By generate the same UUID size, we don't have to do memory ajustment. */ if (strncmp(p, UDN_BTAG, strlen(UDN_BTAG)) == 0) { p += strlen(UDN_BTAG); /* Find the balanced </UDN> */ s = strstr(p, UDN_ETAG); if (s == 0 || (s-p) > sizeof(deviceType)-1) { upnp_syslog(LOG_ERR, "Parse format:<UDN>...</UDN> error.\n"); return -1; } p += 5; /* "uuid:" is 5 char */ if ((s-p) != 36) { upnp_syslog(LOG_ERR, "UUID length is not 36.\n"); return -1; } /* Replace uuid */ upnp_gen_uuid(new_uuid, deviceType); memcpy(p, new_uuid, 36); /* uuid is 36 characters. */ p = p + 36; /* 46 = 10 + 36 */ /* * Afert the UUID of device in description XML is changed, * we have to sync this new UUID to the advertisement table. */ sync_advertise_uuid(device, new_uuid, deviceType); } /* * The SSDP broadcasts not only the device information but * also all the services of this device, which shares the * UUID of that device. * As a result, we have to find out all the <serviceType>'s * of this <deviceType>, and sync the device UUID to the * advertisement table */ if (strncmp(p, SERVICE_BTAG, strlen(SERVICE_BTAG)) == 0) { p += strlen(SERVICE_BTAG); /* Find the balanced </serviceType> */ s = strstr(p, SERVICE_ETAG); if (s == 0 || (s-p) > sizeof(serviceType)-1) { upnp_syslog(LOG_ERR, "Parse format:<serviceType>...</serviceType> error.\n"); return -1; } /* Save serviceType */ strncpy(serviceType, p, s-p); serviceType[s-p] = '\0'; p = s + strlen(SERVICE_ETAG); /* * Found a <serviceType>...</serviceType>. * For example, * <serviceType>urn:schemas-upnp-org:service:Layer3Forwarding:1 * </serviceType> * is the service of * <deviceType>urn:schemas-upnp-org:device:InternetGatewayDevice:1 * </deviceType>. * We have to search the * "urn:schemas-upnp-org:service:Layer3Forwarding:1" * in the advertisement table, and replace its UUID with the UUID of * "urn:schemas-upnp-org:device:InternetGatewayDevice:1". */ sync_advertise_uuid(device, new_uuid, serviceType); } } return 0; }
/* UPnP module initialization */ int upnp_init(UPNP_CONTEXT *context) { UPNP_INTERFACE *ifp = 0; UPNP_DEVICE *device; int i; char ifname_list[256]; char *name, *p, *next; sprintf(xml_InternetGatewayDevice_real,xml_InternetGatewayDevice,nvram_safe_get("DD_BOARD"),nvram_safe_get("router_name"),nvram_safe_get("DD_BOARD")); /* Clear flag */ upnp_flag = 0; /* Clean up */ memset(context, 0, sizeof(*context)); upnp_osl_read_config(&context->config); oslib_ifname_list(ifname_list); /* Do context common initialization */ context->adv_seconds = time(0); context->gena_last_check = time(0); context->ssdp_sock = -1; for (i = 0; (device = upnp_device_table[i]) != 0; i++) { (*device->common_init)(context); /* * Give a unique uuid to this router, * create same uuid when restart */ upnp_device_renew_uuid(context, device); } /* Create the mutlicast receive socket for all interfaces */ if (ssdp_init(context) != 0) goto error_out; for (name = ifname_list, p = name; name && name[0]; name = next, p = 0) { strtok_r(p, " ", &next); ifp = upnp_ifinit(context, name); if (ifp == 0) continue; context->focus_ifp = ifp; /* Perform per interface protocol initialization */ if (upnp_http_init(context) != 0) { upnp_syslog(LOG_ERR, "upnp_http_init::%s init error!", ifp->ifname); goto error_out; } if (ssdp_add_multi(context) == -1) { upnp_syslog(LOG_ERR, "ssdp_init::%s error!", ifp->ifname); goto error_out; } if (upnp_request_init(context) != 0) { upnp_syslog(LOG_ERR, "upnp_request_init::init error!"); goto error_out; } /* * Hook device table to each interface. * The init function of each device * intialize the event variables, and send SSDP ALIVE to each * interface */ for (i = 0; (device = upnp_device_table[i]) != 0; i++) { if (device->attach_mode == DEVICE_ATTACH_ALWAYS) upnp_device_attach(context, device); } } /* No interface found, return directly */ if (context->iflist == 0) { upnp_syslog(LOG_ERR, "No UPnP interface specified, bye!"); goto error_out; } upnp_syslog(LOG_INFO, "UPnP daemon is ready to run"); return 0; error_out: upnp_flag = UPNP_FLAG_SHUTDOWN; return -1; }