C语言实现UPnP自动端口映射

来源:互联网 发布:java界面美化 编辑:程序博客网 时间:2024/06/05 08:38

UPnP端口自动映射程序:1、程序功能: 本质为UPnP Control Point程序,自动搜索IGD(Internet Gateway Device),发送相关命令包,使IGD进行相应Action,实现端口自动映射C语言实现的UPnP端口映射程序,linux平台程序如下:

#include "cmipv4.h"#ifdef _DEBUG#define DBG_INFO(x) printf x#else#define DBG_INFO(x)#endif#define TAGSTART "<%s>"#define TAGEND     "</%s>"#define UPNP_SEARCH_ADDR "239.255.255.250"#define UPNP_SEARCH_PORT 1900#define UPNP_MSG_BUF_SIZE 8192#define UPPN_HTTP_VER  "HTTP/1.1"#define XML_VERSION "1.0"#define RootDeviceUDN "upnp:rootdevice"#define UDNPREFIX "urn:schemas-upnp-org:device:"#define GATEWAYDEVICE0 "InternetGatewayDevice"#define GATEWAYDEVICE1 "WANDevice"#define GATEWAYDEVICE2 "WANConnectionDevice"#define UPNP_VER 1#if 0enum{    deviceType,    presentationURL,    friendlyName,    manufacturer,    manufacturerURL,    modelDescription,    modelName,    modelNumber,    UDN,    UPC,    serviceList,    deviceList,    ALL};enum{    POST,    GET};char *Tag[ALL] = {    "deviceType",    "presentationURL",    "friendlyName",    "manufacturer",    "manufacturerURL",    "modelDescription",    "modelName",    "modelNumber",    "UDN",    "UPC",    "serviceList",    "deviceList"};typedef struct {    int tag;    char *tagName;}XML_Device;XML_Device deviceNodes[ALL] = {    {deviceType, "deviceType"},    {presentationURL, "presentationURL"},    {friendlyName, "friendlyName"},    {manufacturer, "manufacturer"},    {manufacturerURL, "manufacturerURL"},    {modelDescription, "modelDescription"},    {modelName, "modelName"},    {modelNumber, "modelNumber"},    {UDN, "UDN"},    {UPC, "UPC"},    {serviceList, "serviceList"},    {deviceList, "deviceList"}};typedef struct _UPnPService{    char* ServiceType;    char* ServiceId;    char* ControlURL;    char* SubscriptionURL;    char* SCPDURL;    char* SubscriptionID;    int MaxVersion;    struct UPnPAction *Actions;    struct UPnPStateVariable *Variables;    struct UPnPDevice *Parent;    struct UPnPService *Next;}UPnPService;typedef struct _UPnPStateVariable{    struct UPnPStateVariable *Next;    struct UPnPService *Parent;    char* Name;    char **AllowedValues;    int NumAllowedValues;    char* Min;    char* Max;    char* Step;}UPnPStateVariable;typedef struct _UPnPAction{    char* Name;    struct UPnPAction *Next;}UPnPAction;typedef struct _UPnPAllowedValue{    struct UPnPAllowedValue *Next;    char* Value;}UPnPAllowedValue;typedef struct _UPnPDevice{    char *deviceType;    char *presentationURL;    char *friendlyName;    char *manufacturer;    char *manufacturerURL;    char *modelDescription;    char *modelName;    char *modelNumber;    char *UDN;    char *UPC;    struct _UPnPDevice *Parent;    struct _UPnPDevice *EmbededDevices;    struct _UPnPDevice *Next;    UPnPService *Services;}UPnPDevice;typedef struct _XMLNode{    char *name;    int nameLen;    struct _XMLNode *parent;    struct _XMLNode *next;    struct _XMLNode *peer;    }XMLNode;typedef struct{    char *name;    int nameLen;    char *value;    int valueLen;    XMLNode *parent;    }XML_PROPERTY;typedef struct{    char searchTarget[64];    char host[16];    }UPnPSearchPara;#endiftypedef struct{    char ExternalIP[16];    unsigned short ExternalPort;    int Protocol;//0:TCP 1:UDP    char InternalIP[16];    unsigned short InternalPort;    int Enabled;//    char Description[64];    long Duration;}UPNP_PORTMAPINFO;    char *SearchFormat = "M-SEARCH * HTTP/1.1\r\n\MX: 5\r\n\ST: %s\r\n\HOST: 239.255.255.250:1900\r\n\MAN: \"ssdp:discover\"\r\n\Content-Length: 0\r\n\\r\n";char *GetFormat = "GET /%s HTTP/1.1\r\n\Accept: text/xml, application/xml\r\n\User-Agent: HC-NVS\r\n\Host: %s:%d\r\n\Connection: Keep-Alive\r\n\Cache-Control: max-age=0\r\n\\r\n";    char *HttpFormat ="POST %s HTTP/1.1\r\n\Host: %s:%d\r\n\SOAPACTION: \"%s#%s\"\r\n\CONTENT-TYPE: text/xml; charset=\"utf-8\"\r\n\Content-Length: %d\r\n\r\n";    char *SoapFormat ="<?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=\"%s\">\r\n%s</u:%s>\r\n</s:Body>\r\n</s:Envelope>\r\n";static char controlUrl[1024] ="";static char controlService[1024] = "";static char deviceAddr[1024] = "";static char deviceIP[16] = "";static char externalIP[16] = "";static int devicePort;static char postUrl[512] = "";static char location[128] = "";int UPnP_GetPropertyByName(char *xml, char *name, char *value){    char *startTag = NULL;    char *endTag = NULL;    //int start;    //int end;    char *p1 = NULL;    char *p2 = NULL;    //int valueLen;        startTag = (char*)malloc(strlen(name)+3);    if(!startTag)        return ERROR;    endTag = (char*)malloc(strlen(name)+4);    if(!endTag)        return ERROR;    sprintf(startTag, "<%s>", name);    sprintf(endTag, "</%s>", name);    if(!(p1 = strstr(xml, startTag)))    {        free(startTag);        free(endTag);        return ERROR;    }    if(!(p2 = strstr(p1, endTag)))    {        free(startTag);        free(endTag);        return ERROR;    }    //size enough    memcpy(value, p1+strlen(startTag), p2-p1-strlen(startTag));    value[p2-p1-strlen(startTag)] = '\0';    //printf("%s\n",value);    free(startTag);    free(endTag);    return strlen(value);}int UPnP_InvokeCmd(char *cmd, char *response, int responsesize){    int upnp_sock;    int ret;    int len;    int contentLen;    int headLen;    char *p = NULL;    struct in_addr addr;    if(!cmd||!response)        return ERROR;    len = strlen(cmd);        ret = inet_pton(AF_INET, deviceIP, &addr);    if(ret <= 0)    {        printf("IGD IP not valid.\n");        return ERROR;    }    if(devicePort<=0||devicePort>65535)    {        printf("IGD Port not valid.\n");        return ERROR;    }    upnp_sock = cmLink(inet_addr(deviceIP), devicePort, 0, 3000);    if(upnp_sock == ERROR)    {        DBG_INFO(("upnp link device create socket failed.\n"));        return ERROR;    }    //printf("after link host.\n");    /*Send  packet*/    ret = cmSend(upnp_sock, cmd, len);    if(ret != OK)    {        DBG_INFO(("UPnP send failed.\n"));        goto __failed;    }    ret = cmWait(upnp_sock, 20000);    if(ret <= 0)    {        DBG_INFO(("sock timeout .\n"));        goto __failed;    }    len = cmRecv(upnp_sock, response, responsesize);    if(len <= 0)    {        DBG_INFO(("receive error.\n"));        goto __failed;    }    p = strstr(response, "\r\n\r\n");    if(!p)    {        return ERROR;    }    headLen = p-response+strlen("\r\n\r\n");        p = strstr(response, "Content-Length:");    if(p)    {        sscanf(p, "%*[^0-9]%d", &contentLen);        if(len<contentLen+headLen)        {            ret = cmRead(upnp_sock, response+strlen(response), contentLen-(strlen(response)-headLen));            if(ret != OK)            {                DBG_INFO(("UPnP Read XML content failed.\n"));                goto __failed;            }        }    }        DBG_INFO(("<<<<<<<<<<<:\n%s\n", response));    if(upnp_sock>=0)    {        cmClose(upnp_sock);        upnp_sock = -1;    }        if((p = strstr(response, "200 OK")) == NULL)    {        return ERROR;    }    return OK;    __failed:     if(upnp_sock>=0)    {        cmClose(upnp_sock);        upnp_sock = -1;    }    return ERROR;}int UPnP_GetDeviceDescription(){    int ret;    char *p  = NULL;    char *p1 = NULL;    char value[1024];    char request[1024];    char tempbuf[10240];    char URLBase[1024];    /*build GET packet*/    sprintf(request, GetFormat, postUrl, deviceIP,devicePort);    ret = strlen(request);    request[ret] = '\0';    DBG_INFO((">>>>>>>> \n%s\n", request));        memset(tempbuf, 0, 10240);    ret = UPnP_InvokeCmd(request, tempbuf, 10240);    if(ret != OK)    {        DBG_INFO(("UPnP invoke Get description failed.\n"));        return ERROR;    }    /*Response state is 200 OK ?*/    if((p = strstr(tempbuf, "200 OK")) == NULL)        return ERROR;    /*Parse Xml, get service contral URL*/    ret = UPnP_GetPropertyByName(tempbuf, "URLBase", URLBase);    //printf("ret = %d\n", ret);    if(ret <= 0)    {        sprintf(URLBase, "http://%s:%d", deviceIP, devicePort);    }    //if(URLBase[strlen(URLBase)-1] != '/')    //{    //    strcat(URLBase, "/");    //}    DBG_INFO(("URLBase is :%s.\n",URLBase));    p = strstr(tempbuf, "<deviceType>");    UPnP_GetPropertyByName(p, "deviceType", value);    if(!strcmp(value, "urn:schemas-upnp-org:device:InternetGatewayDevice:1"))    {        p1 = strstr(p+strlen(value)+25, "<deviceType>");        UPnP_GetPropertyByName(p1, "deviceType", value);        //printf("%s.\n", value);        if(!strcmp(value, "urn:schemas-upnp-org:device:WANDevice:1"))        {            p = strstr(p1+strlen(value)+25, "<deviceType>");            UPnP_GetPropertyByName(p, "deviceType", value);            if(!strcmp(value, "urn:schemas-upnp-org:device:WANConnectionDevice:1"))            {                p1 = strstr(p+strlen(value)+25, "<serviceType>");                UPnP_GetPropertyByName(p1, "serviceType", value);                if(!strcmp(value, "urn:schemas-upnp-org:service:WANIPConnection:1")                    ||!strcmp(value, "urn:schemas-upnp-org:service:WANPPPConnection:1"))                {                    strcpy(controlService, value);                    p = strstr(p1+strlen(value), "<controlURL>");                    ret = UPnP_GetPropertyByName(p, "controlURL", value);                    if(ret == 0)                        return ERROR;                    strcpy(postUrl, value);                    strcat(URLBase, value);                    strcpy(controlUrl, URLBase);                    DBG_INFO(("controlUrl is %s.\n", controlUrl));                    //sscanf(URLBase, "%*[^0-9]%[^:]:%d/%s", ip, &port, postUrl);                    //printf("%s.\n", postUrl);                    return OK;                }            }        }    }    return ERROR;        }int UPnP_GetExternalIPAddress(){    char out[10240];    char temp[10240];    char *p = NULL;    char *p1 = NULL;    int len;    int ret;    sprintf(temp, SoapFormat,               "GetExternalIPAddress",               controlService,               "",               "GetExternalIPAddress");    len = strlen(temp);        sprintf(out, HttpFormat,              postUrl,              deviceIP,              devicePort,              controlService,              "GetExternalIPAddress",              len);        strcat(out, temp);    len = strlen(out);    DBG_INFO((">>>>>>>>>>>>\n%s.\n", out));    memset(temp, 0, 10240);    ret = UPnP_InvokeCmd(out, temp, 10240);    if(ret != OK)    {        DBG_INFO(("UPnP invoke GetExternalIP failed.\n"));        return ERROR;    }        if((p = strstr(temp, "200 OK")) == NULL)    {        return ERROR;    }    if((p = strstr(temp, "<NewExternalIPAddress"))!=NULL)    {        p1 = strstr(p, ">");        sscanf(p1, "%*[^0-9]%[^<]", externalIP);    }        return OK;    }#if 1int UPnP_GetPortMappingInfo( int PortMappingIndex, UPNP_PORTMAPINFO *info){    char out[10240];    char temp[10240];    char *p = NULL;    char *p1 = NULL;    int len;    int ret;    char Action_GetPort_Format[64] = {"<NewPortMappingIndex>%d</NewPortMappingIndex>"};    sprintf(out, Action_GetPort_Format, PortMappingIndex);    len = strlen(out);    sprintf(temp, SoapFormat, "GetGenericPortMappingEntry", controlService, out, "GetGenericPortMappingEntry");    len = strlen(temp);    sprintf(out, HttpFormat,                     postUrl,                     deviceIP,                     devicePort,                     controlService,                     "GetGenericPortMappingEntry",                     len);    strcat(out, temp);    len = strlen(out);    DBG_INFO((">>>>>>>>>>>>\n%s.\n", out));    memset(temp, 0, 10240);    ret = UPnP_InvokeCmd(out, temp, 10240);    if(ret != OK)    {        DBG_INFO(("UPnP invoke GetPortMappingInfo failed.\n"));        return ERROR;    }        if((p = strstr(temp, "200 OK")) == NULL)    {        return ERROR;    }    else    {        if((p = strstr(temp, "<NewExternalPort")) != NULL)        {            p1 = strstr(p, ">");            sscanf(p1, "%*[^0-9]%d", &info->ExternalPort);        }        if((p = strstr(temp, "<NewProtocol"))!=NULL)        {            p1 = strstr(p, ">");            if(!strncmp("TCP", p1+1, 3))                info->Protocol = 0;            else if(!strncmp("UDP", p1+1, 3))                info->Protocol = 1;        }        if((p = strstr(temp, "<NewInternalPort"))!=NULL)        {            p1 = strstr(p, ">");            sscanf(p1, "%*[^0-9]%d", &info->InternalPort);        }        if((p = strstr(temp, "<NewInternalClient"))!=NULL)        {            p1 = strstr(p, ">");            sscanf(p1+1, "%[^<]", info->InternalIP);        }        if((p = strstr(temp, "<NewEnabled"))!=NULL)        {            p1 = strstr(p, ">");            sscanf(p1, "%*[^0-9]%d", &info->Enabled);        }        if((p = strstr(temp, "<NewPortMappingDescription"))!=NULL)        {            p1 = strstr(p ,">");            sscanf(p1+1, "%[^<][64]", info->Description);        }        if((p = strstr(temp, "<NewLeaseDuration>"))!=NULL)        {            p1 = strstr(p, ">");            sscanf(p1, "%*[^0-9]%d", &info->Duration);        }    }    return OK;}int UPnP_PrintPortMap(){    int i = 0;    int ret;    UPNP_PORTMAPINFO info;    printf("\n\n---------------------------------------------------------------\n");    printf("This Internet Gateway Device's UPnP PortMapping Information is :\n");    printf("---------------------------------------------------------------\n");    printf("\n%s   %s   %s      %s         %s   %s \n", "Protocol", "ExternalPort", "InternalIP", "InternalPort", "Description", "Ena");    while(1)    {        ret = UPnP_GetPortMappingInfo(i++, &info);        if(ret != OK)        {            printf("---------------------------------------------------------------\n\n");            break;        }        else        {            printf("%5s%13d%20s%12d%24.22s%5d\n", (info.Protocol == 0)?"TCP":"UDP", info.ExternalPort, info.InternalIP, info.InternalPort, info.Description, info.Enabled);        }    }    return OK;}#endifint UPnP_AddPortMapping(const char *RemoteHose,                                                     unsigned short ExternalPort,                                                    const char *PortMappingProtocal,                                                    unsigned short InternalPort,                                                    const char *InternalClient,                                                    const char *PortMappingDescription,                                                    int PortMappingEnable,                                                    unsigned long PortMappingLeaseDuration){    char out[10240];    char temp[10240];    char *p = NULL;    int len;    int ret;    char Action_AddPort_Format[512] = {"<NewRemoteHost></NewRemoteHost>\r\n\<NewExternalPort>%d</NewExternalPort>\r\n\<NewProtocol>%s</NewProtocol>\r\n\<NewInternalPort>%d</NewInternalPort>\r\n\<NewInternalClient>%s</NewInternalClient>\r\n\<NewEnabled>1</NewEnabled>\r\n\<NewPortMappingDescription>%s</NewPortMappingDescription>\r\n\<NewLeaseDuration>%d</NewLeaseDuration>\r\n"};    sprintf(out, Action_AddPort_Format,              ExternalPort,              PortMappingProtocal,              InternalPort,               InternalClient,              PortMappingDescription,              PortMappingLeaseDuration);    len = strlen(out);        sprintf(temp, SoapFormat,               "AddPortMapping",               controlService,               out,               "AddPortMapping");    len = strlen(temp);        sprintf(out, HttpFormat,              postUrl,              deviceIP,              devicePort,              controlService,              "AddPortMapping",              len);        strcat(out, temp);    len = strlen(out);    DBG_INFO((">>>>>>>>>>>>\n%s.\n", out));    memset(temp, 0, 10240);    ret = UPnP_InvokeCmd(out, temp, 10240);    if(ret != OK)    {        DBG_INFO(("UPnP invoke AddPortMapping failed.\n"));        return ERROR;    }        if((p = strstr(temp, "200 OK")) == NULL)        return ERROR;        return OK;}int UPnP_FindDevice(){    int len, ret;    char *p = NULL;    char *p1 = NULL;    int upnp_localsock, upnp_localport;    struct sockaddr_in qAddr, addr;    char Location[128];    char buf[UPNP_MSG_BUF_SIZE] = {0};    bzero((char*)&qAddr, sizeof(qAddr));    qAddr.sin_family = AF_INET;    qAddr.sin_port = htons(UPNP_SEARCH_PORT);    qAddr.sin_addr.s_addr = inet_addr(UPNP_SEARCH_ADDR);    /*create udp sock*/    upnp_localsock = cmUdpStart(33445);    if(upnp_localsock < 0)    {        DBG_INFO(("UPnP sock create failed.\n"));        goto __failed;    }    /*join multicast*/    ret = cmJoin(upnp_localsock, inet_addr(UPNP_SEARCH_ADDR));    if(ret < 0)    {        DBG_INFO(("UPnP join failed.\n"));        goto __failed;    }    /*build and send search(IGD) msg */    sprintf(buf,  SearchFormat, "upnp:rootdevice"/*"urn:schemas-upnp-org:device:InternetGatewayDevice:1"*/);    len = strlen(buf);    DBG_INFO((">>>>>>>>>>:\n%s\n",buf));            ret = cmSendto(upnp_localsock, buf, len, &qAddr);    ret = cmSendto(upnp_localsock, buf, len, &qAddr);    if(ret == ERROR)    {        DBG_INFO(("UPnP send search packet failed.\n"));        goto __failed;    }        /*receive response for search*/    ret = cmWait(upnp_localsock, 7000);    if(ret <= 0)    {        DBG_INFO(("sock 7s timeout .\n"));        goto __failed;    }    memset(buf, 0, UPNP_MSG_BUF_SIZE);    ret = cmRecvfrom(upnp_localsock, buf, UPNP_MSG_BUF_SIZE, &addr);    if(ret <= 0)    {        DBG_INFO(("UPnP receive response for search faield.\n"));        goto __failed;    }    buf[ret] = '\0';    DBG_INFO(("<<<<<<<<<\n%s\n",buf));    /*search session over, close the socket*/    if(upnp_localsock>=0)    {        cmClose(upnp_localsock);        upnp_localsock = -1;    }    /*sample  is http OK*/    if(strstr(buf, "200 OK") == NULL)    {        DBG_INFO(("UPnP receive bad http response. failed.\n"));        return ERROR;    }        /*find Location value*/    p = strstr(buf, "Location");    if(!p)        return ERROR;    p1 = strstr(p, "\r\n");    if(!p1)        return ERROR;    memcpy(Location, p, p1-p);    Location[p1-p] = '\0';    DBG_INFO(("UPnP get Location: %s.\n", Location));    //get description accroding host:port\path    strcpy(deviceAddr, Location);    p = strstr(Location, "http://");    if(!p)    {        DBG_INFO(("Location not viladate"));        return ERROR;    }    //parse ip port post    sscanf(p, "%*[^0-9]%[^:]:%d/%s", deviceIP, &devicePort, postUrl);    DBG_INFO(("Device location %s:%d/%s\n", deviceIP, devicePort, postUrl));    return OK;    __failed:     if(upnp_localsock>=0)    {        cmClose(upnp_localsock);        upnp_localsock = -1;    }    return ERROR;        }int main(int argc, char *argv[]){    char inteIP[16];    char extIP[16];    int intePort;    int extPort;    char protocol[4];    char description[16];    int enabled;    int ret;    int i;    struct in_addr addr;          char cmd;    for(i=0;i<3;i++)    {        ret = UPnP_FindDevice();        if(ret == OK)        {            UPnP_GetDeviceDescription();            break;        }    }        if(ret != OK)    {        printf("\nThere is no Internet Gateway Device finded.You can try again later.Quit.\n");        return ERROR;    }    UPnP_GetExternalIPAddress();    if((strlen(externalIP) != 0)&&(strlen(deviceIP) != 0))    {        printf("Find A Internet Gateway Device. External IP is %s. Internal IP is %s.\n", externalIP, deviceIP);    }    else    {        printf("There is no Internet Gateway Device finded.You can try again later.Quit.\n");        return ERROR;    }        if(argc > 1)    {        if(strlen(argv[1]) != 2)        {            printf("cmd error.\n");            return ERROR;        }        ret = sscanf(argv[1], "-%c", &cmd);        if(ret !=1)        {            printf("cmd error.\n");            return ERROR;        }        switch(cmd)        {            case 'a':                if(argc != 6+2)                {                    printf("AddPortMapping Action CMD Error.\n");                    break;                }                else                {                    if(strcmp(argv[2], "TCP")&&strcmp(argv[2], "UDP"))                    {                        printf("AddPortMapping Action CMD Error.---Protocol must is TCP or UDP.\n");                        break;                    }                    else                        strcpy(protocol, argv[2]);                                        if((ret = sscanf(argv[3], "%d", &extPort)) != 1)                    {                        printf("AddPortMapping Action CMD Error.---ExternalPort must is a int 0-65535.\n");                        break;                    }                    ret = inet_pton(AF_INET, argv[4], &addr);                    if(ret <= 0)                    {                        printf("AddPortMapping Action CMD Error.---InternalIP not valid.\n");                        break;                    }                    else                        strcpy(inteIP, argv[4]);                    if((ret = sscanf(argv[5], "%d", &intePort)) != 1)                    {                        printf("AddPortMapping Action CMD Error.---InternalPort must is a int 0-65535.\n");                        break;                    }                    if(strlen(argv[6]) >=15)                    {                        printf("AddPortMapping Action CMD Error.---description too long. ONLY GET 15 chars");                        strncpy(description, argv[6], 15);                    }                    else                        strcpy(description, argv[6]);                    if((strcmp(argv[7], "1"))&&(strcmp(argv[7], "0")))                    {                        printf("AddPortMapping Action CMD Error.---Enabled must 1 or 0.\n");                        break;                    }                    else                        sscanf(argv[7], "%d", &enabled);                    UPnP_AddPortMapping("", extPort, protocol, intePort, inteIP, description, enabled, 0);                    printf("AddPortMapping Action Successed.\n");                }                break;            case 'i':                UPnP_PrintPortMap();                break;            case 'h':                printf("\nUPnP Port Mapping Tool.\n");                printf("\t-a Add Port Mapping.\n\t\teg.upnp -a TCP{protocol} 8000{ExternalPort} 192.168.10.11{InternalIP} 8000{InternalPort} NVS-Manage{Description} 1{Enabled}\n");                printf("\t-i Print Port Mapping Infomation.\n\t\teg.upnp -i\n");                break;            default:                printf("NO such cmd. use upnp -h get help info.\n");                break;        }            }    return 0;}