📄 miniupnpc.c
字号:
/* $Id: miniupnpc.c,v 1.45 2007/10/16 15:23:44 nanard Exp $ *//* Project : miniupnp * Author : Thomas BERNARD * copyright (c) 2005-2007 Thomas Bernard * This software is subjet to the conditions detailed in the * provided LICENCE file. */#include <stdio.h>#include <stdlib.h>#include <string.h>#ifdef WIN32#include <winsock2.h>#include <Ws2tcpip.h>#include <io.h>#define snprintf _snprintf#define strncasecmp memicmp#define MAXHOSTNAMELEN 64#else#include <unistd.h>#include <sys/socket.h>#include <sys/types.h>#include <sys/param.h>#include <netinet/in.h>#include <arpa/inet.h>#include <poll.h>#include <netdb.h>#define closesocket close#endif#include "miniupnpc.h"#include "minissdpc.h"#include "miniwget.h"#include "minisoap.h"#include "minixml.h"#include "upnpcommands.h"/* Uncomment the following to transmit the msearch from the same port * as the UPnP multicast port. With WinXP this seems to result in the * responses to the msearch being lost, thus if things dont work then * comment this out. *//* #define TX_FROM_UPNP_PORT */#ifdef WIN32#define PRINT_SOCKET_ERROR(x) printf("Socket error: %s, %d\n", x, WSAGetLastError());#else#define PRINT_SOCKET_ERROR(x) perror(x)#endif/* root description parsing */void parserootdesc(const char * buffer, int bufsize, struct IGDdatas * data){ struct xmlparser parser; /* xmlparser object */ parser.xmlstart = buffer; parser.xmlsize = bufsize; parser.data = data; parser.starteltfunc = IGDstartelt; parser.endeltfunc = IGDendelt; parser.datafunc = IGDdata; parser.attfunc = 0; parsexml(&parser);#ifndef NDEBUG printIGD(data);#endif}/* Content-length: nnn */static int getcontentlenfromline(const char * p, int n){ static const char contlenstr[] = "content-length"; const char * p2 = contlenstr; int a = 0; while(*p2) { if(n==0) return -1; if(*p2 != *p && *p2 != (*p + 32)) return -1; p++; p2++; n--; } if(n==0) return -1; if(*p != ':') return -1; p++; n--; while(*p == ' ') { if(n==0) return -1; p++; n--; } while(*p >= '0' && *p <= '9') { if(n==0) return -1; a = (a * 10) + (*p - '0'); p++; n--; } return a;}static voidgetContentLengthAndHeaderLength(char * p, int n, int * contentlen, int * headerlen){ char * line; int linelen; int r; line = p; while(line < p + n) { linelen = 0; while(line[linelen] != '\r' && line[linelen] != '\r') { if(line+linelen >= p+n) return; linelen++; } r = getcontentlenfromline(line, linelen); if(r>0) *contentlen = r; line = line + linelen + 2; if(line[0] == '\r' && line[1] == '\n') { *headerlen = (line - p) + 2; return; } }}/* simpleUPnPcommand : * not so simple ! * */int simpleUPnPcommand(int s, const char * url, const char * service, const char * action, struct UPNParg * args, char * buffer, int * bufsize){ struct sockaddr_in dest; char hostname[MAXHOSTNAMELEN+1]; unsigned short port = 0; char * path; char soapact[128]; char soapbody[2048]; int soapbodylen; char * buf; int buffree; int n; int contentlen, headerlen; /* for the response */ snprintf(soapact, sizeof(soapact), "%s#%s", service, action); if(args==NULL) { soapbodylen = snprintf(soapbody, sizeof(soapbody), "<?xml version=\"1.0\"?>\r\n" "<SOAP-ENV:Envelope " "xmlns:SOAP-ENV=\"http://schemas.xmlsoap.org/soap/envelope/\" " "SOAP-ENV:encodingStyle=\"http://schemas.xmlsoap.org/soap/encoding/\">" "<SOAP-ENV:Body>" "<m:%s xmlns:m=\"%s\"/>" "</SOAP-ENV:Body></SOAP-ENV:Envelope>" "\r\n", action, service); } else { char * p; const char * pe, * pv; soapbodylen = snprintf(soapbody, sizeof(soapbody), "<?xml version=\"1.0\"?>\r\n" "<SOAP-ENV:Envelope " "xmlns:SOAP-ENV=\"http://schemas.xmlsoap.org/soap/envelope/\" " "SOAP-ENV:encodingStyle=\"http://schemas.xmlsoap.org/soap/encoding/\">" "<SOAP-ENV:Body>" "<m:%s xmlns:m=\"%s\">", action, service); p = soapbody + soapbodylen; while(args->elt) { /* check that we are never overflowing the string... */ if(soapbody + sizeof(soapbody) <= p + 100) { /* we keep a margin of at least 100 bytes */ *bufsize = 0; return -1; } *(p++) = '<'; pe = args->elt; while(*pe) *(p++) = *(pe++); *(p++) = '>'; if((pv = args->val)) { while(*pv) *(p++) = *(pv++); } *(p++) = '<'; *(p++) = '/'; pe = args->elt; while(*pe) *(p++) = *(pe++); *(p++) = '>'; args++; } *(p++) = '<'; *(p++) = '/'; *(p++) = 'm'; *(p++) = ':'; pe = action; while(*pe) *(p++) = *(pe++); strncpy(p, "></SOAP-ENV:Body></SOAP-ENV:Envelope>\r\n", soapbody + sizeof(soapbody) - p); } if(!parseURL(url, hostname, &port, &path)) return -1; if(s<0) { s = socket(PF_INET, SOCK_STREAM, 0); dest.sin_family = AF_INET; dest.sin_port = htons(port); dest.sin_addr.s_addr = inet_addr(hostname); if(connect(s, (struct sockaddr *)&dest, sizeof(struct sockaddr))<0) { PRINT_SOCKET_ERROR("connect"); *bufsize = 0; return -1; } } n = soapPostSubmit(s, path, hostname, port, soapact, soapbody); contentlen = -1; headerlen = -1; buf = buffer; buffree = *bufsize; *bufsize = 0; while ((n = ReceiveData(s, buf, buffree, 5000)) > 0) { buffree -= n; buf += n; *bufsize += n; getContentLengthAndHeaderLength(buffer, *bufsize, &contentlen, &headerlen);#ifdef DEBUG printf("n=%d bufsize=%d ContLen=%d HeadLen=%d\n", n, *bufsize, contentlen, headerlen);#endif if(contentlen > 0 && headerlen > 0 && *bufsize >= contentlen+headerlen) break; } closesocket(s); return -1;}/* parseMSEARCHReply() * the last 4 arguments are filled during the parsing : * - location/locationsize : "location:" field of the SSDP reply packet * - st/stsize : "st:" field of the SSDP reply packet. * The strings are NOT null terminated */static voidparseMSEARCHReply(const char * reply, int size, const char * * location, int * locationsize, const char * * st, int * stsize){ int a, b, i; i = 0; a = i; /* start of the line */ b = 0; while(i<size) { switch(reply[i]) { case ':': if(b==0) { b = i; /* end of the "header" */ /*for(j=a; j<b; j++) { putchar(reply[j]); } */ } break; case '\x0a': case '\x0d': if(b!=0) { /*for(j=b+1; j<i; j++) { putchar(reply[j]); } putchar('\n');*/ do { b++; } while(reply[b]==' '); if(0==strncasecmp(reply+a, "location", 8)) { *location = reply+b; *locationsize = i-b; } else if(0==strncasecmp(reply+a, "st", 2)) { *st = reply+b; *stsize = i-b; } b = 0; } a = i+1; break; default: break; } i++; }}/* port upnp discover : SSDP protocol */#define PORT (1900)#define UPNP_MCAST_ADDR "239.255.255.250"/* upnpDiscover() : * return a chained list of all devices found or NULL if * no devices was found. * It is up to the caller to free the chained list * delay is in millisecond (poll) */struct UPNPDev * upnpDiscover(int delay, const char * multicastif){ struct UPNPDev * tmp; struct UPNPDev * devlist = 0; int opt = 1; static const char MSearchMsgFmt[] = "M-SEARCH * HTTP/1.1\r\n" "HOST: " UPNP_MCAST_ADDR ":" "1900" "\r\n" "ST: %s\r\n" "MAN: \"ssdp:discover\"\r\n" "MX: 3\r\n" "\r\n"; static const char * const deviceList[] = { "urn:schemas-upnp-org:device:InternetGatewayDevice:1", "urn:schemas-upnp-org:service:WANIPConnection:1", "urn:schemas-upnp-org:service:WANPPPConnection:1", "upnp:rootdevice", 0 }; int deviceIndex = 0; char bufr[1536]; /* reception and emission buffer */ int sudp; int n; struct sockaddr_in sockudp_r, sockudp_w;#ifndef WIN32 /* first try to get infos from minissdpd ! */ devlist = getDevicesFromMiniSSDPD(deviceList[0], "/var/run/minissdpd.sock"); if(devlist) return devlist;#endif /* fallback to direct discovery */#ifdef WIN32 sudp = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP);#else sudp = socket(PF_INET, SOCK_DGRAM, 0);#endif if(sudp < 0)
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -