static int ForceTermination(UFILE *uclient, PService psvc, PAction ac, pvar_entry_t args, int nargs) { uint success = TRUE; /* assume no error will occur */ PWANIPConnectionData pdata = (PWANIPConnectionData) psvc->opaque; //PWANDevicePrivateData pdevdata = (PWANDevicePrivateData) psvc->device->parent->opaque; char wanproto[100]; /* Our ConnectionType is always IP_Routed, so I don't need to check that here. */ if (pdata->connection_status == IP_DISCONNECTED) { soap_error(uclient, SOAP_CONNECTIONALREADYTERMNATED); success = FALSE; } else if (pdata->connection_status != IP_CONNECTED && pdata->connection_status != IP_CONNECTING) { soap_error(uclient, SOAP_DISCONNECTINPROGRESS); success = FALSE; } else { igd_pri_wan_var(wanproto, sizeof(wanproto), "proto"); /* Save the wan_proto into NVRAM to restore when the igd is enabled */ nvram_set("upnp_wan_proto", nvram_safe_get(wanproto)); nvram_set(wanproto, "disabled"); req_nvram_commit(1); igd_restart(3); } return success; }
int DeletePortMapping( UFILE *uclient, PService psvc, PAction ac, pvar_entry_t args, int nargs) /* {"NewRemoteHost", VAR_RemoteHost, VAR_IN}, */ /* {"NewExternalPort", VAR_ExternalPort, VAR_IN}, */ /* {"NewProtocol", VAR_PortMappingProtocol, VAR_IN}, */ { int i, parse_status, status = 0; netconf_nat_t e; mapping_t mapping; // bypass port mapping when NAT is disabled if (nvram_invmatch("wan_nat_x", "1")) return 0; parse_status = (int) parse_dnat(&e, ac->params[2].value, /* NewProtocol */ ac->params[0].value, /* NewRemoteHost */ ac->params[1].value, NULL, /* NewExternalPort */ NULL, /* NewInternalClient */ NULL, NULL /* NewInternalPort */ ); if (!parse_status) { status = SOAP_INVALID_ARGS; } else { status = SOAP_NOSUCHENTRYINARRAY; for (i = 0; mapmgr_get_port_map(i, &mapping); i++) { if (SameMatchInfo(&e, (netconf_nat_t*)&mapping)) { mapmgr_delete_port_map(i); status = 0; /* SUCCESS! */ } } } if (status) soap_error( uclient, status ); return (status == 0); }
/** * Cancel a SOAP RPC. */ void soap_rpc_cancel(soap_rpc_t *sr) { soap_rpc_check(sr); soap_error(sr, SOAP_E_CANCELLED); }
static int RequestConnection(UFILE *uclient, PService psvc, PAction ac, pvar_entry_t args, int nargs) { uint success = TRUE; /* assume no error will occur */ PWANIPConnectionData pdata = psvc->opaque; char wanproto[100], *str; if (pdata->connection_status == IP_UNCONFIGURED) { soap_error(uclient, SOAP_CONNECTIONNOTCONFIGURED); success = FALSE; } else if (pdata->connection_status == IP_DISCONNECTED) { igd_pri_wan_var(wanproto, sizeof(wanproto), "proto"); /* Save the wan_proto into NVRAM to restore when the igd is enabled */ if ((str = nvram_get("upnp_wan_proto")) == NULL) nvram_set(wanproto, "dhcp"); /* pick dhcp as defualt */ else nvram_set(wanproto, str); req_nvram_commit(1); igd_restart(3); pdata->connection_status = IP_CONNECTING; pdata->connection_timeout = CONNECTION_TIMEOUT; mark_changed(psvc, VAR_ConnectionStatus); } mark_changed(psvc, VAR_ConnectionStatus); return success; }
int GetSpecificPortMappingEntry(UFILE *uclient, PService psvc, PAction ac, pvar_entry_t args, int nargs) /* {"NewRemoteHost", VAR_RemoteHost, VAR_IN}, */ /* {"NewExternalPort", VAR_ExternalPort, VAR_IN}, */ /* {"NewProtocol", VAR_PortMappingProtocol, VAR_IN}, */ /* {"NewInternalPort", VAR_InternalPort, VAR_OUT}, */ /* {"NewInternalClient", VAR_InternalClient, VAR_OUT}, */ /* {"NewEnabled", VAR_PortMappingEnabled, VAR_OUT}, */ /* {"NewPortMappingDescription", VAR_PortMappingDescription, VAR_OUT}, */ /* {"NewLeaseDuration", VAR_PortMappingLeaseDuration, VAR_OUT}, */ { int i, parse_status, status; netconf_nat_t e; mapping_t mapping; const unsigned char *bytep; static char InternalClient[16]; static char InternalPort[7]; static char Enabled[2]; static char Description[60]; parse_status = (int) parse_dnat(&e, ac->params[2].value, /* NewProtocol */ ac->params[0].value, /* NewRemoteHost */ ac->params[1].value, NULL, /* NewExternalPort */ NULL, /* NewInternalClient */ NULL, NULL /* NewInternalPort */ ); if (!parse_status) { status = SOAP_INVALID_ARGS; } else { status = SOAP_NOSUCHENTRYINARRAY; for (i = 0; mapmgr_get_port_map(i, &mapping); i++) { if (SameMatchInfo(&e, (netconf_nat_t*)&mapping)) { InternalClient[0] = '\0'; if (mapping.ipaddr.s_addr) { bytep = (const unsigned char *) &(mapping.ipaddr); snprintf(InternalClient, sizeof(InternalClient), "%d.%d.%d.%d", bytep[0], bytep[1], bytep[2], bytep[3]); } snprintf(InternalPort, sizeof(InternalPort), "%d", ntohs(mapping.ports[0])); snprintf(Enabled, sizeof(Enabled), "%d", ((mapping.match.flags & NETCONF_DISABLED) ? 0 : 1)); strncpy(Description, mapping.desc, sizeof(Description)); ac->params[3].value = InternalPort; ac->params[4].value = InternalClient; ac->params[5].value = Enabled; ac->params[6].value = Description; ac->params[7].value = "0"; status = 0; /* SUCCESS! */ break; } } } if (status) soap_error( uclient, status ); return (status == 0); }
int GetGenericPortMappingEntry(UFILE *uclient, PService psvc, PAction ac, pvar_entry_t args, int nargs) /* {"NewPortMappingIndex", VAR_PortMappingNumberOfEntries, VAR_IN}, */ /* {"NewRemoteHost", VAR_RemoteHost, VAR_OUT}, */ /* {"NewExternalPort", VAR_ExternalPort, VAR_OUT}, */ /* {"NewProtocol", VAR_PortMappingProtocol, VAR_OUT}, */ /* {"NewInternalPort", VAR_InternalPort, VAR_OUT}, */ /* {"NewInternalClient", VAR_InternalClient, VAR_OUT}, */ /* {"NewEnabled", VAR_PortMappingEnabled, VAR_OUT}, */ /* {"NewPortMappingDescription", VAR_PortMappingDescription, VAR_OUT}, */ /* {"NewLeaseDuration", VAR_PortMappingLeaseDuration, VAR_OUT}, */ { int success = TRUE; char *PortMappingIndex = ac->params[0].value; mapping_t mapping; u_int32 i; const unsigned char *bytep; static char RemoteHost[16]; static char ExternalPort[7]; static char InternalClient[16]; static char InternalPort[7]; static char Enabled[2]; static char Description[60]; i = atoi(PortMappingIndex); if (mapmgr_get_port_map(i, &mapping)) { RemoteHost[0] = '\0'; if (mapping.match.src.ipaddr.s_addr) { bytep = (const unsigned char *) &(mapping.match.src.ipaddr); snprintf(RemoteHost, sizeof(RemoteHost), "%d.%d.%d.%d", bytep[0], bytep[1], bytep[2], bytep[3]); } InternalClient[0] = '\0'; if (mapping.ipaddr.s_addr) { bytep = (const unsigned char *) &(mapping.ipaddr); snprintf(InternalClient, sizeof(InternalClient), "%d.%d.%d.%d", bytep[0], bytep[1], bytep[2], bytep[3]); } snprintf(ExternalPort, sizeof(ExternalPort), "%d", ntohs(mapping.match.dst.ports[0])); snprintf(InternalPort, sizeof(InternalPort), "%d", ntohs(mapping.ports[0])); snprintf(Enabled, sizeof(Enabled), "%d", ((mapping.match.flags & NETCONF_DISABLED) ? 0 : 1)); snprintf(Description, sizeof(Description), "%s", mapping.desc); ac->params[1].value = RemoteHost; ac->params[2].value = ExternalPort; ac->params[3].value = (mapping.match.ipproto == IPPROTO_TCP ? "TCP" : "UDP" ); ac->params[4].value = InternalPort; ac->params[5].value = InternalClient; ac->params[6].value = Enabled; ac->params[7].value = Description; ac->params[8].value = "0"; } else { soap_error( uclient, SOAP_SPECIFIEDARRAYINDEXINVALID ); success = FALSE; } return success; }
/** * Delayed RPC start. */ static void soap_rpc_launch(cqueue_t *unused_cq, gpointer obj) { soap_rpc_t *sr = obj; http_post_data_t post; (void) unused_cq; soap_rpc_check(sr); sr->delay_ev = NULL; if (GNET_PROPERTY(soap_debug) > 4) { g_debug("SOAP \"%s\" at \"%s\": launching (%s)", sr->action, sr->url, sr->retry ? "retry" : "initial"); } sr->reply_len = 0; /* In case we retry, clear out older data */ /* * Launch the asynchronous POST request. */ post.content_type = SOAP_CONTENT_TYPE; post.data = pmsg_start(sr->mb); post.datalen = pmsg_size(sr->mb); post.data_free = NULL; post.data_free_arg = NULL; sr->ha = http_async_post(sr->url, &post, soap_header_ind, soap_data_ind, soap_error_ind); /* * If we cannot create the HTTP request, it can be the URL is wrong, * or no connection can be established to the host. Hence it's a * contacting error, not an I/O error at this stage. */ if (sr->ha == NULL) { if (GNET_PROPERTY(soap_debug)) { g_warning("SOAP cannot contact \"%s\": %s", sr->url, http_async_strerror(http_async_errno)); } soap_error(sr, SOAP_E_CONTACT); return; } /* * Customize the HTTP layer. */ http_async_set_opaque(sr->ha, sr, NULL); http_async_set_op_post_request(sr->ha, soap_build_request); http_async_set_op_headsent(sr->ha, soap_sent_head); http_async_set_op_datasent(sr->ha, soap_sent_data); http_async_set_op_gotreply(sr->ha, soap_got_reply); http_async_option_ctl(sr->ha, HTTP_O_READ_REPLY, HTTP_CTL_ADD); }
/** * HTTP async callback, invoked on errors. */ static void soap_error_ind(http_async_t *ha, http_errtype_t type, void *val) { soap_rpc_t *sr = http_async_get_opaque(ha); soap_error_t err = SOAP_E_OK; soap_rpc_check(sr); if (GNET_PROPERTY(soap_debug)) { http_async_log_error_dbg(ha, type, val, "SOAP", GNET_PROPERTY(soap_debug) > 1); } if (HTTP_ASYNC_ERROR == type) { switch (GPOINTER_TO_INT(val)) { case HTTP_ASYNC_CANCELLED: /* * Retry with M-POST if cancelled with sr->retry set to TRUE. */ if (sr->retry) { g_assert(NULL == sr->delay_ev); sr->delay_ev = cq_main_insert(1, soap_rpc_launch, sr); if (GNET_PROPERTY(soap_debug) > 1) { g_message("SOAP \"%s\" at \"%s\": retrying with M-POST", sr->action, sr->url); } } break; /* No callback on explicit user cancel */ case HTTP_ASYNC_DATA2BIG: err = SOAP_E_DATA2BIG; break; case HTTP_ASYNC_CONN_TIMEOUT: case HTTP_ASYNC_TIMEOUT: err = SOAP_E_TIMEOUT; break; default: err = SOAP_E_TRANSPORT; break; } } else { err = SOAP_E_TRANSPORT; } if (err != SOAP_E_OK) soap_error(sr, err); }
int AddPortMapping( UFILE *uclient, PService psvc, PAction ac, pvar_entry_t args, int nargs) /* {"NewRemoteHost", VAR_RemoteHost, VAR_IN}, */ /* {"NewExternalPort", VAR_ExternalPort, VAR_IN}, */ /* {"NewProtocol", VAR_PortMappingProtocol, VAR_IN}, */ /* {"NewInternalPort", VAR_InternalPort, VAR_IN}, */ /* {"NewInternalClient", VAR_InternalClient, VAR_IN}, */ /* {"NewEnabled", VAR_PortMappingEnabled, VAR_IN}, */ /* {"NewPortMappingDescription", VAR_PortMappingDescription, VAR_IN}, */ /* {"NewLeaseDuration", VAR_PortMappingLeaseDuration, VAR_IN}, */ { int i, parse_status, status = 0; char *LeaseDuration = ac->params[7].value; char *Description = ac->params[6].value; bool bEnabled = (bool) atoi(ac->params[5].value); bool found_match; netconf_nat_t e; mapping_t mapping; // bypass port mapping when NAT is disabled if (nvram_invmatch("wan_nat_x", "1")) return 0; do { if (atoi(LeaseDuration) != 0) { status = SOAP_ONLYPERMANENTLEASESSUPPORTED; continue; } parse_status = (int) parse_dnat(&e, ac->params[2].value, /* NewProtocol */ ac->params[0].value, /* NewRemoteHost */ ac->params[1].value, NULL, /* NewExternalPort */ ac->params[4].value, /* NewInternalClient */ ac->params[3].value, NULL /* NewInternalPort */ ); if (!parse_status) { status = SOAP_INVALID_ARGS; continue; } found_match = FALSE; for (i = 0; mapmgr_get_port_map(i, &mapping); i++) { if (SameMatchInfo(&e, (netconf_nat_t*)&mapping)) { found_match = TRUE; break; } } if (found_match) { if (!SameInternalClient(&e, &mapping)) { // we already have that mapping, but a different target...error. status = SOAP_CONFLICTINMAPPINGENTRY; continue; } else { mapmgr_delete_port_map(i); } } /* Enabled/disable this mapping according to the "NewEnabled" parameter. */ if (bEnabled) { e.match.flags &= ~NETCONF_DISABLED; } else { e.match.flags |= NETCONF_DISABLED; } strncpy(e.desc, Description, sizeof(e.desc)); mapmgr_add_port_map(&e); } while(0); if (status) soap_error( uclient, status ); return (status == 0); }
/** * Process the SOAP reply from the server. */ static void soap_process_reply(soap_rpc_t *sr) { const char *buf; vxml_parser_t *vp; vxml_error_t e; xnode_t *root = NULL; xnode_t *xn = NULL; const char *charset; soap_rpc_check(sr); if (sr->reply_len != 0 && (GNET_PROPERTY(soap_trace) & SOCK_TRACE_IN)) { g_debug("----Got SOAP HTTP reply data from %s:", sr->url); if (log_printable(LOG_STDERR)) { fwrite(sr->reply_data, sr->reply_len, 1, stderr); fputs("----End SOAP HTTP reply\n", stderr); } } if (GNET_PROPERTY(soap_debug) > 2) { g_debug("SOAP \"%s\" at \"%s\": processing reply (%lu byte%s) HTTP %d", sr->action, sr->url, (unsigned long) sr->reply_len, 1 == sr->reply_len ? "" : "s", sr->http_code); } /* * If we got a 2xx reply, we need to parse up to the <Body> element * and then pass up the remaining to the user for parsing specific * elemnts accordingly. * * Other reply codes indicate an error. On 4xx replies we may not * have any XML to parse. On 5xx replies, we should usually have * a <Fault> indication under the <Body>. * * The strategy used here is to parse the XML reply into a tree and then * analyse the tree, ignoring the HTTP status code which is redundant. */ buf = header_get(sr->header, "Content-Type"); if (NULL == buf) goto no_xml; /* * MIME type and subtypes are case-insensitive (see RFC 2616, section 3.7). */ if ( !http_field_starts_with(buf, SOAP_TEXT_REPLY, FALSE) && !http_field_starts_with(buf, SOAP_APPLICATION_REPLY, FALSE) ) { if (GNET_PROPERTY(soap_debug)) { g_debug("SOAP \"%s\" at \"%s\": got unexpected Content-Type: %s", sr->action, sr->url, buf); } goto no_xml; } /* * Extract charset if given. */ charset = http_parameter_get(buf, "charset"); /* * Parse the SOAP envelope. */ vp = vxml_parser_make(sr->action, VXML_O_STRIP_BLANKS); vxml_parser_add_data(vp, sr->reply_data, sr->reply_len); if (!vxml_parser_set_charset(vp, charset)) { g_warning("SOAP \"%s\" at \"%s\": ignoring unknown charset \"%s\"", sr->action, sr->url, charset); } e = vxml_parse_tree(vp, &root); vxml_parser_free(vp); if (e != VXML_E_OK) { if (GNET_PROPERTY(soap_debug)) { g_debug("SOAP \"%s\" at \"%s\": cannot parse XML reply: %s", sr->action, sr->url, vxml_strerror(e)); } goto bad_xml; } g_assert(root != NULL); /* * Make sure we got a SOAP reply. */ if (!xnode_is_element_named(root, SOAP_NAMESPACE, SOAP_X_ENVELOPE)) goto not_soap; /* * Look for the <SOAP:Body> element. */ for (xn = xnode_first_child(root); TRUE; xn = xnode_next_sibling(xn)) { if (NULL == xn || !xnode_within_namespace(xn, SOAP_NAMESPACE)) goto bad_soap; if (0 == strcmp(SOAP_X_BODY, xnode_element_name(xn))) break; } /* * Inspect the first child of the <SOAP:Body> element. * * If it's a <SOAP:Fault>, go process it and return an error. * If it's another SOAP tag, we have an unknown structure. * Otherwise it's the reply, for user code to handle. */ xn = xnode_first_child(xn); if (NULL == xn) goto bad_soap; if (xnode_is_element_named(xn, SOAP_NAMESPACE, SOAP_X_FAULT)) { xnode_detach(xn); soap_fault(sr, xn); } else if (xnode_within_namespace(xn, SOAP_NAMESPACE)) { goto bad_soap; } else { xnode_detach(xn); soap_reply(sr, xn); } xnode_tree_free(root); return; not_soap: if (GNET_PROPERTY(soap_debug)) { g_debug("SOAP \"%s\" at \"%s\": unexpected root XML " "element <%s:%s>", sr->action, sr->url, EMPTY_STRING(xnode_element_ns(root)), xnode_element_name(root)); } xnode_tree_free(root); /* FALL THROUGH */ no_xml: soap_error(sr, SOAP_E_PROTOCOL); return; bad_soap: if (GNET_PROPERTY(soap_debug)) { g_debug("SOAP \"%s\" at \"%s\": unexpected XML structure", sr->action, sr->url); } if (GNET_PROPERTY(soap_debug) > 1) { g_debug("SOAP current node is %s", xnode_to_string(xn)); } if (GNET_PROPERTY(soap_debug) > 2) xfmt_tree_dump(root, stderr); xnode_tree_free(root); /* FALL THROUGH */ bad_xml: soap_error(sr, SOAP_E_PROCESSING); return; }
/* Parse up the "Body>". The element immediately after that will be the name of the action to invoke, possibly preceded by a namespace. The action name may be followed by whitespace and other attributes, like namespace, etc., or it may immediatly by followed by the closing angle-bracket '>'. The elements contains within the action name are the arguments, also possibly preceded by a namespace (although the SOAP spec says they should not be) The list of argument will be terminated by a closing element that corresponds to the action name, also possibly preceded by a namespace. */ void soap_action(UFILE *up, PService psvc, char *soapaction, char *body) { char *ac, *u, *actstart, *colon; var_entry_t args[10]; int nargs = 0; char *argstart, *argend; char *valstart, *valend; char *sp, *eb; char pattern[80]; // split out the action name. if (soapaction != NULL && (ac = index(soapaction, '#')) != NULL) { ac++; snprintf(pattern, sizeof(pattern), ":Body>"); if ((u = strstr(body, pattern)) != NULL) { body = u + 1; if ((u = strstr(body, "<")) != NULL) { body = u + 1; actstart = body; sp = index(body, ' '); eb = index(body, '>'); if (sp && eb) { if (sp < eb) *sp = '\0'; body = eb + 1; if (body[-2] != '/') { snprintf(pattern, sizeof(pattern), "</%s", actstart); do { // parse each argument and put it into a list of <name, value> pointers. if ((argstart = strstr(body, "<")) == NULL) break; // if we are at the end of the argument list, // break out of the loop. if (strncmp(argstart, pattern, strlen(pattern)) == 0) break; argstart++; // advance over the '<' // although it is contrary to the spec, // some argument elements may have a namespace prefix. // in partcular, QueryStateVariable actions from windows XP // appear to do this but actions from Messenger do not. // In any case, be prepared to skip the namespace prefix. sp = index(argstart, ' '); eb = index(argstart, '>'); if (sp && eb && sp < eb) *sp = '\0'; *eb = '\0'; argend = eb; // DO NOT move this above the setting of argend to '/0'!!!! if ((colon = index(argstart, ':')) != NULL) argstart = colon + 1; if (argend[-1] == '/') { valstart = valend = argend; } else { valstart = argend + 1; if ((valend = index(valstart, '<')) == NULL) break; *valend = '\0'; } // put this argument into the arg list. args[nargs].name = argstart; args[nargs].value = valstart; nargs++; // point to the beginning of the next argument. body = valend + 1; } while (*body); } } } } // invoke that action UPNP_ACTION(psvc, ac, args, nargs); if ( strcmp(ac, "QueryStateVariable") == 0 ) { QueryStateVariable( up, psvc, ac, args, nargs); } else { dispatch( up, psvc, ac, args, nargs ); } if ((psvc->flags & VAR_CHANGED) == VAR_CHANGED) { update_all_subscriptions(psvc); } } else { soap_error( up, SOAP_INVALID_ACTION ); } }