📄 upnp.c
字号:
/**
* @file upnp.c UPnP Implementation
* @ingroup core
*
* gaim
*
* Gaim is the legal property of its developers, whose names are too numerous
* to list here. Please refer to the COPYRIGHT file distributed with this
* source distribution.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include "platform.h"
#include "xmlnode.h"
#include "util.h"
#include "upnp.h"
#include "error.h"
#include "ip.h"
#include <curl/curl.h>
#define TRUE GNUNET_YES
#define FALSE GNUNET_NO
#define g_return_if_fail(a) if(!(a)) return;
#define g_return_val_if_fail(a, val) if(!(a)) return (val);
#define HTTP_OK "200 OK"
#define NUM_UDP_ATTEMPTS 2
#define HTTPMU_HOST_ADDRESS "239.255.255.250"
#define HTTPMU_HOST_PORT 1900
#define SEARCH_REQUEST_DEVICE "urn:schemas-upnp-org:service:%s"
#define SEARCH_REQUEST_STRING \
"M-SEARCH * HTTP/1.1\r\n" \
"MX: 2\r\n" \
"HOST: 239.255.255.250:1900\r\n" \
"MAN: \"ssdp:discover\"\r\n" \
"ST: urn:schemas-upnp-org:service:%s\r\n" \
"\r\n"
#define WAN_IP_CONN_SERVICE "WANIPConnection:1"
#define WAN_PPP_CONN_SERVICE "WANPPPConnection:1"
#define HTTP_POST_SOAP_HEADER \
"SOAPACTION: \"urn:schemas-upnp-org:service:%s#%s\""
#define HTTP_POST_SIZE_HEADER "CONTENT-LENGTH: %u"
#define SOAP_ACTION \
"<?xml version=\"1.0\" encoding=\"utf-8\"?>\r\n" \
"<s:Envelope xmlns:s=\"http://schemas.xmlsoap.org/soap/envelope/\" " \
"s:encodingStyle=\"http://schemas.xmlsoap.org/soap/encoding/\">\r\n" \
"<s:Body>\r\n" \
"<u:%s xmlns:u=\"urn:schemas-upnp-org:service:%s\">\r\n" \
"%s" \
"</u:%s>\r\n" \
"</s:Body>\r\n" \
"</s:Envelope>"
#define PORT_MAPPING_LEASE_TIME "0"
#define PORT_MAPPING_DESCRIPTION "GNUNET_UPNP_PORT_FORWARD"
#define ADD_PORT_MAPPING_PARAMS \
"<NewRemoteHost></NewRemoteHost>\r\n" \
"<NewExternalPort>%i</NewExternalPort>\r\n" \
"<NewProtocol>%s</NewProtocol>\r\n" \
"<NewInternalPort>%i</NewInternalPort>\r\n" \
"<NewInternalClient>%s</NewInternalClient>\r\n" \
"<NewEnabled>1</NewEnabled>\r\n" \
"<NewPortMappingDescription>" \
PORT_MAPPING_DESCRIPTION \
"</NewPortMappingDescription>\r\n" \
"<NewLeaseDuration>" \
PORT_MAPPING_LEASE_TIME \
"</NewLeaseDuration>\r\n"
#define DELETE_PORT_MAPPING_PARAMS \
"<NewRemoteHost></NewRemoteHost>\r\n" \
"<NewExternalPort>%i</NewExternalPort>\r\n" \
"<NewProtocol>%s</NewProtocol>\r\n"
typedef enum
{
GAIM_UPNP_STATUS_UNDISCOVERED = -1,
GAIM_UPNP_STATUS_UNABLE_TO_DISCOVER,
GAIM_UPNP_STATUS_DISCOVERING,
GAIM_UPNP_STATUS_DISCOVERED
} GaimUPnPStatus;
typedef struct
{
GaimUPnPStatus status;
char *control_url;
const char *service_type;
char publicip[16];
} GaimUPnPControlInfo;
typedef struct
{
const char *service_type;
char *full_url;
char *buf;
unsigned int buf_len;
int sock;
} UPnPDiscoveryData;
static GaimUPnPControlInfo control_info = {
GAIM_UPNP_STATUS_UNDISCOVERED,
NULL,
NULL,
"",
};
/**
* This is the signature used for functions that act as a callback
* to CURL.
*/
typedef size_t (*GaimUtilFetchUrlCallback) (void *url_data,
size_t size,
size_t nmemb, void *user_data);
static char *
g_strstr_len (const char *haystack, int haystack_len, const char *needle)
{
int i;
g_return_val_if_fail (haystack != NULL, NULL);
g_return_val_if_fail (needle != NULL, NULL);
if (haystack_len < 0)
return strstr (haystack, needle);
else
{
const char *p = haystack;
int needle_len = strlen (needle);
const char *end = haystack + haystack_len - needle_len;
if (needle_len == 0)
return (char *) haystack;
while (*p && p <= end)
{
for (i = 0; i < needle_len; i++)
if (p[i] != needle[i])
goto next;
return (char *) p;
next:
p++;
}
}
return NULL;
}
static int
gaim_upnp_compare_device (const xmlnode * device, const char *deviceType)
{
xmlnode *deviceTypeNode = xmlnode_get_child (device, "deviceType");
char *tmp;
int ret;
if (deviceTypeNode == NULL)
return FALSE;
tmp = xmlnode_get_data (deviceTypeNode);
ret = !strcasecmp (tmp, deviceType);
GNUNET_free (tmp);
return ret;
}
static int
gaim_upnp_compare_service (const xmlnode * service, const char *serviceType)
{
xmlnode *serviceTypeNode;
char *tmp;
int ret;
if (service == NULL)
return FALSE;
serviceTypeNode = xmlnode_get_child (service, "serviceType");
if (serviceTypeNode == NULL)
return FALSE;
tmp = xmlnode_get_data (serviceTypeNode);
ret = !strcasecmp (tmp, serviceType);
GNUNET_free (tmp);
return ret;
}
static char *
gaim_upnp_parse_description_response (const char *httpResponse,
size_t len,
const char *httpURL,
const char *serviceType)
{
char *xmlRoot, *baseURL, *controlURL, *service;
xmlnode *xmlRootNode, *serviceTypeNode, *controlURLNode, *baseURLNode;
char *tmp;
/* find the root of the xml document */
xmlRoot = g_strstr_len (httpResponse, len, "<root");
if (xmlRoot == NULL)
return NULL;
if (g_strstr_len (httpResponse, len, "</root") == NULL)
return NULL;
/* create the xml root node */
xmlRootNode = xmlnode_from_str (xmlRoot, len - (xmlRoot - httpResponse));
if (xmlRootNode == NULL)
return NULL;
/* get the baseURL of the device */
baseURLNode = xmlnode_get_child (xmlRootNode, "URLBase");
if (baseURLNode != NULL)
{
baseURL = xmlnode_get_data (baseURLNode);
}
else
{
baseURL = GNUNET_strdup (httpURL);
}
/* get the serviceType child that has the service type as its data */
/* get urn:schemas-upnp-org:device:InternetGatewayDevice:1 and its devicelist */
serviceTypeNode = xmlnode_get_child (xmlRootNode, "device");
while (!gaim_upnp_compare_device (serviceTypeNode,
"urn:schemas-upnp-org:device:InternetGatewayDevice:1")
&& serviceTypeNode != NULL)
{
serviceTypeNode = xmlnode_get_next_twin (serviceTypeNode);
}
if (serviceTypeNode == NULL)
{
GNUNET_free (baseURL);
xmlnode_free (xmlRootNode);
return NULL;
}
serviceTypeNode = xmlnode_get_child (serviceTypeNode, "deviceList");
if (serviceTypeNode == NULL)
{
GNUNET_free (baseURL);
xmlnode_free (xmlRootNode);
return NULL;
}
/* get urn:schemas-upnp-org:device:WANDevice:1 and its devicelist */
serviceTypeNode = xmlnode_get_child (serviceTypeNode, "device");
while (!gaim_upnp_compare_device (serviceTypeNode,
"urn:schemas-upnp-org:device:WANDevice:1")
&& serviceTypeNode != NULL)
{
serviceTypeNode = xmlnode_get_next_twin (serviceTypeNode);
}
if (serviceTypeNode == NULL)
{
GNUNET_free (baseURL);
xmlnode_free (xmlRootNode);
return NULL;
}
serviceTypeNode = xmlnode_get_child (serviceTypeNode, "deviceList");
if (serviceTypeNode == NULL)
{
GNUNET_free (baseURL);
xmlnode_free (xmlRootNode);
return NULL;
}
/* get urn:schemas-upnp-org:device:WANConnectionDevice:1 and its servicelist */
serviceTypeNode = xmlnode_get_child (serviceTypeNode, "device");
while (serviceTypeNode && !gaim_upnp_compare_device (serviceTypeNode,
"urn:schemas-upnp-org:device:WANConnectionDevice:1"))
{
serviceTypeNode = xmlnode_get_next_twin (serviceTypeNode);
}
if (serviceTypeNode == NULL)
{
GNUNET_free (baseURL);
xmlnode_free (xmlRootNode);
return NULL;
}
serviceTypeNode = xmlnode_get_child (serviceTypeNode, "serviceList");
if (serviceTypeNode == NULL)
{
GNUNET_free (baseURL);
xmlnode_free (xmlRootNode);
return NULL;
}
/* get the serviceType variable passed to this function */
service = g_strdup_printf (SEARCH_REQUEST_DEVICE, serviceType);
serviceTypeNode = xmlnode_get_child (serviceTypeNode, "service");
while (!gaim_upnp_compare_service (serviceTypeNode, service) &&
serviceTypeNode != NULL)
{
serviceTypeNode = xmlnode_get_next_twin (serviceTypeNode);
}
GNUNET_free (service);
if (serviceTypeNode == NULL)
{
GNUNET_free (baseURL);
xmlnode_free (xmlRootNode);
return NULL;
}
/* get the controlURL of the service */
if ((controlURLNode = xmlnode_get_child (serviceTypeNode,
"controlURL")) == NULL)
{
GNUNET_free (baseURL);
xmlnode_free (xmlRootNode);
return NULL;
}
tmp = xmlnode_get_data (controlURLNode);
if (baseURL && !gaim_str_has_prefix (tmp, "http://") &&
!gaim_str_has_prefix (tmp, "HTTP://"))
{
if (tmp[0] == '/')
{
size_t len;
const char *end;
/* absolute path */
end = strstr (&baseURL[strlen ("http://")], "/");
if (end == NULL)
len = strlen (&baseURL[strlen ("http://")]);
else
len = end - &baseURL[strlen ("http://")];
controlURL = g_strdup_printf ("http://%.*s%s",
len,
&baseURL[strlen ("http://")], tmp);
}
else
{
controlURL = g_strdup_printf ("%s%s", baseURL, tmp);
}
GNUNET_free (tmp);
}
else
{
controlURL = tmp;
}
GNUNET_free (baseURL);
xmlnode_free (xmlRootNode);
return controlURL;
}
#define CURL_EASY_SETOPT(c, a, b) do { ret = curl_easy_setopt(c, a, b); if (ret != CURLE_OK) GNUNET_GE_LOG(NULL, GNUNET_GE_WARNING | GNUNET_GE_USER | GNUNET_GE_BULK, _("%s failed at %s:%d: `%s'\n"), "curl_easy_setopt", __FILE__, __LINE__, curl_easy_strerror(ret)); } while (0);
/**
* Do the generic curl setup.
*/
static int
setup_curl (const char *proxy, CURL * curl)
{
int ret;
CURL_EASY_SETOPT (curl, CURLOPT_FAILONERROR, 1);
if (strlen (proxy) > 0)
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -