/** * Declare user-defined mapping between a URI and a namespace. */ static void xfmt_prefix_declare(struct xfmt_pass2 *xp2, const char *uri, const char *prefix) { nv_pair_t *nv; nv = nv_table_lookup(xp2->uri2prefix, uri); if (nv != NULL) { /* * Silently ignore the mapping if we already have seen an identical one * in the XML tree during the first pass. */ if (strcmp(prefix, nv_pair_value_str(nv)) != 0) { g_carp("XFMT ignoring supplied prefix '%s' for '%s': " "already saw '%s' in the tree", prefix, uri, nv_pair_value_str(nv)); } } else { /* * New mapping. */ nv = nv_pair_make_static_str(uri, prefix); nv_table_insert_pair(xp2->uri2prefix, nv); } }
/** * Record a tree-defined mapping between a prefix and a namespace URI. */ static void xfmt_prefix_record(struct xfmt_pass1 *xp1, const char *prefix, const char *uri) { nv_pair_t *nv; /* * Our policy is to use one single prefix for a given namespace URI * throughout the document. Although several prefixes could be used. * this is confusing to read and serves no value: a human will be mislead * into thinking the two namespaces are different because they carry * distinct prefixes, and a machine will not care about the prefix value. */ nv = nv_table_lookup(xp1->uri2prefix, uri); if (nv != NULL) { /* * Silently ignore the mapping if we already have seen an identical one * in the XML tree. */ if (strcmp(prefix, nv_pair_value_str(nv)) != 0) { g_carp("XFMT ignoring prefix '%s' for '%s': " "already saw '%s' earlier in the tree", prefix, uri, nv_pair_value_str(nv)); } } else { /* * New mapping. */ nv = nv_pair_make_static_str(uri, prefix); nv_table_insert_pair(xp1->uri2prefix, nv); } }
/** * Add a port forwarding (*:ext_port -> addr:port) [IP or PPP connection]. * * @param usd the UPnP service to contact * @param proto mapping protocol * @param ext_port the mapped external port for which we want the mapping * @param int_addr the internal client address * @param int_port the internal port for which we want the mapping * @param desc comment description * @param lease lease duration (0 = permanent) * @param cb callback to invoke when reply is available * @param arg additional callback argument * * @return UPnP request handle if the SOAP RPC was initiated, NULL otherwise * (in which case callbacks will never be called). */ upnp_ctrl_t * upnp_ctrl_AddPortMapping(const upnp_service_t *usd, enum upnp_map_proto proto, guint16 ext_port, host_addr_t int_addr, guint16 int_port, const char *desc, time_delta_t lease, upnp_ctrl_cb_t cb, void *arg) { nv_pair_t *argv[8]; char ext_port_buf[UINT16_DEC_BUFLEN]; char int_port_buf[UINT16_DEC_BUFLEN]; char int_addr_buf[HOST_ADDR_BUFLEN]; char lease_buf[UINT32_DEC_BUFLEN]; const char *protocol; const char *description; g_assert(lease >= 0); g_assert(lease <= MAX_INT_VAL(gint32)); g_assert(ext_port != 0); g_assert(int_port != 0); int32_to_string_buf(ext_port, ext_port_buf, sizeof ext_port_buf); int32_to_string_buf(int_port, int_port_buf, sizeof int_port_buf); host_addr_to_string_buf(int_addr, int_addr_buf, sizeof int_addr_buf); protocol = upnp_map_proto_to_string(proto); int32_to_string_buf(lease, lease_buf, sizeof lease_buf); description = str_smsg("%s (%s)", desc, protocol); argv[0] = nv_pair_make_static_str(ARG_REMOTE_HOST, EMPTY); /* Wildcard */ argv[1] = nv_pair_make_static_str(ARG_EXTERNAL_PORT, ext_port_buf); argv[2] = nv_pair_make_static_str(ARG_PROTOCOL, protocol); argv[3] = nv_pair_make_static_str(ARG_INTERNAL_PORT, int_port_buf); argv[4] = nv_pair_make_static_str(ARG_INTERNAL_CLIENT, int_addr_buf); argv[5] = nv_pair_make_static_str(ARG_ENABLED, ONE); /* Enable! */ argv[6] = nv_pair_make_static_str(ARG_PORTMAP_DESC, description); argv[7] = nv_pair_make_static_str(ARG_LEASE_DURATION, lease_buf); /* * TODO: when talking to a v2 WANIPConnection service, we can use * the AddAnyPortMapping() call. This will require that GTKG maintains * knowledge about the remote port so that it can advertise that remote * port instead of the local listening port. * * Attempts must be made to get the same external port for both TCP and UDP, * or this will create problems to servents assuming that they will always * be identical (like GTKG does when it uses the TCP listening port of * a remote host to send a push-proxy request via UDP).. * --RAM, 2011-01-18 */ return upnp_ctrl_launch(usd, "AddPortMapping", argv, G_N_ELEMENTS(argv), cb, arg, NULL); }
/** * Get information about a specific port mapping [IP or PPP connection]. * * @param usd the UPnP service to contact * @param proto mapping protocol * @param port the mapped external port for which we want the mapping * @param cb callback to invoke when reply is available * @param arg additional callback argument * * @return UPnP request handle if the SOAP RPC was initiated, NULL otherwise * (in which case callbacks will never be called). */ upnp_ctrl_t * upnp_ctrl_GetSpecificPortMappingEntry(const upnp_service_t *usd, enum upnp_map_proto proto, guint16 port, upnp_ctrl_cb_t cb, void *arg) { nv_pair_t *argv[3]; char buf[UINT16_DEC_BUFLEN]; const char *protocol; int32_to_string_buf(port, buf, sizeof buf); protocol = upnp_map_proto_to_string(proto); argv[0] = nv_pair_make_static_str(ARG_REMOTE_HOST, EMPTY); /* Wildcard */ argv[1] = nv_pair_make_static_str(ARG_EXTERNAL_PORT, buf); argv[2] = nv_pair_make_static_str(ARG_PROTOCOL, protocol); return upnp_ctrl_launch(usd, "GetSpecificPortMappingEntry", argv, G_N_ELEMENTS(argv), cb, arg, upnp_ctrl_ret_GetSpecificPortMappingEntry); }
/** * Declare association between a prefix and a namespace URI at * the current depth. * * @param xp2 the pass 2 context * @param prefix declared prefix string * @param uri namespace URI * @param free_prefix whether the prefix string will have to be freed */ static void xfmt_ns_declare(struct xfmt_pass2 *xp2, const char *prefix, const char *uri, bool free_prefix) { nv_pair_t *nv; bool inserted; /* * The prefix string is shared between the two symbol tables, and is * optionally freed when the pair is removed from the uris table. * Therefore, removal must be done on the prefixes symbol table first. */ nv = nv_pair_make_static_str(prefix, uri); inserted = symtab_insert_pair(xp2->prefixes, nv, xp2->depth); g_assert(inserted); nv = nv_pair_make_static_str(uri, prefix); if (free_prefix) nv_pair_set_value_free(nv, xfmt_nv_free); inserted = symtab_insert_pair(xp2->uris, nv, xp2->depth); g_assert(inserted); }
/** * Successful SOAP RPC reply callback. * * @param sr the SOAP RPC request * @param root XML tree of SOAP reply * @param arg the UPnP control request */ static void upnp_ctrl_soap_reply(const soap_rpc_t *sr, xnode_t *root, void *arg) { upnp_ctrl_t *ucd = arg; xnode_t *xn; nv_table_t *nvt; void *reply; size_t reply_len; host_addr_t local_addr; int code; upnp_ctrl_check(ucd); if (GNET_PROPERTY(upnp_debug) > 1) { g_debug("UPNP got SOAP reply for %s", ucd->action); if (GNET_PROPERTY(upnp_debug) > 2) xfmt_tree_dump(root, stderr); } ucd->sr = NULL; /* Done with SOAP request */ if (soap_rpc_local_addr(sr, &local_addr)) upnp_set_local_addr(local_addr); /* * Decompile the returned values. * * <u:actionResponse xmlns:u="urn:schemas-upnp-org:service:serviceType:v"> * <arg1>out value1</arg1> * <arg2>out value2</arg2> * : : : : * <argn>out valuen</argn> * </u:actionResponse> * * Values are inserted in name / value pairs: "arg1" -> "out value 1" and * given to the launch callback for extracting and decompiling the values. */ nvt = nv_table_make(TRUE); for (xn = xnode_first_child(root); xn; xn = xnode_next_sibling(xn)) { nv_pair_t *nv; xnode_t *xt; if (!xnode_is_element(xn)) { if (GNET_PROPERTY(upnp_debug)) { g_warning("UPNP \"%s\" skipping XML node %s", ucd->action, xnode_to_string(xn)); } continue; } xt = xnode_first_child(xn); if (NULL == xt || !xnode_is_text(xt)) { if (GNET_PROPERTY(upnp_debug)) { g_warning("UPNP \"%s\" bad child node %s in %s", ucd->action, xnode_to_string(xt), xnode_to_string2(xn)); } } else { /* * Name/value strings point in the tree, which is going to be * alive for the duration of the processing, so we can use the * strings without copying them. */ nv = nv_pair_make_static_str( xnode_element_name(xn), xnode_text(xt)); nv_table_insert_pair(nvt, nv); if (xnode_next_sibling(xt) != NULL) { if (GNET_PROPERTY(upnp_debug)) { g_warning("UPNP \"%s\" content of %s is not pure text", ucd->action, xnode_to_string(xt)); } } } } /* * Attempt to decompile the replied values, if any are expected. * * Allocated data is done via walloc(), and the returned structure is flat. * It will be freed after invoking the user callback. */ if (ucd->lcb != NULL) { reply = (*ucd->lcb)(nvt, &reply_len); code = NULL == reply ? UPNP_ERR_OK : UPNP_ERR_BAD_REPLY; } else { code = UPNP_ERR_OK; reply = NULL; reply_len = 0; } /* * Let UPnP control invoker know about the result of the query. */ (*ucd->cb)(code, reply, reply_len, ucd->cb_arg); /* * Done, final cleanup. */ WFREE_NULL(reply, reply_len); nv_table_free(nvt); upnp_ctrl_free(ucd); }