miniupnpc.c 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969
  1. /* $Id: miniupnpc.c,v 1.85 2010/12/21 16:13:14 nanard Exp $ */
  2. /* Project : miniupnp
  3. * Author : Thomas BERNARD
  4. * copyright (c) 2005-2010 Thomas Bernard
  5. * This software is subjet to the conditions detailed in the
  6. * provided LICENSE file. */
  7. #define __EXTENSIONS__ 1
  8. #if !defined(MACOSX) && !defined(__sun)
  9. #if !defined(_XOPEN_SOURCE) && !defined(__OpenBSD__) && !defined(__NetBSD__)
  10. #ifndef __cplusplus
  11. #define _XOPEN_SOURCE 600
  12. #endif
  13. #endif
  14. #ifndef __BSD_VISIBLE
  15. #define __BSD_VISIBLE 1
  16. #endif
  17. #endif
  18. #include <stdlib.h>
  19. #include <stdio.h>
  20. #include <string.h>
  21. #ifdef WIN32
  22. /* Win32 Specific includes and defines */
  23. #include <winsock2.h>
  24. #include <ws2tcpip.h>
  25. #include <io.h>
  26. #include <iphlpapi.h>
  27. #define snprintf _snprintf
  28. #if defined(_MSC_VER) && (_MSC_VER >= 1400)
  29. #define strncasecmp _memicmp
  30. #else /* defined(_MSC_VER) && (_MSC_VER >= 1400) */
  31. #define strncasecmp memicmp
  32. #endif /* defined(_MSC_VER) && (_MSC_VER >= 1400) */
  33. #define MAXHOSTNAMELEN 64
  34. #else /* #ifdef WIN32 */
  35. /* Standard POSIX includes */
  36. #include <unistd.h>
  37. #if defined(__amigaos__) && !defined(__amigaos4__)
  38. /* Amiga OS 3 specific stuff */
  39. #define socklen_t int
  40. #else
  41. #include <sys/select.h>
  42. #endif
  43. #include <sys/socket.h>
  44. #include <sys/types.h>
  45. #include <sys/param.h>
  46. #include <netinet/in.h>
  47. #include <arpa/inet.h>
  48. #include <netdb.h>
  49. #if !defined(__amigaos__) && !defined(__amigaos4__)
  50. #include <poll.h>
  51. #endif
  52. #include <strings.h>
  53. #include <errno.h>
  54. #define closesocket close
  55. #define MINIUPNPC_IGNORE_EINTR
  56. #endif /* #else WIN32 */
  57. #ifdef MINIUPNPC_SET_SOCKET_TIMEOUT
  58. #include <sys/time.h>
  59. #endif
  60. #if defined(__amigaos__) || defined(__amigaos4__)
  61. /* Amiga OS specific stuff */
  62. #define TIMEVAL struct timeval
  63. #endif
  64. #include "miniupnpc.h"
  65. #include "minissdpc.h"
  66. #include "miniwget.h"
  67. #include "minisoap.h"
  68. #include "minixml.h"
  69. #include "upnpcommands.h"
  70. #include "connecthostport.h"
  71. #ifdef WIN32
  72. #define PRINT_SOCKET_ERROR(x) printf("Socket error: %s, %d\n", x, WSAGetLastError());
  73. #else
  74. #define PRINT_SOCKET_ERROR(x) perror(x)
  75. #endif
  76. #define SOAPPREFIX "s"
  77. #define SERVICEPREFIX "u"
  78. #define SERVICEPREFIX2 'u'
  79. /* root description parsing */
  80. LIBSPEC void parserootdesc(const char * buffer, int bufsize, struct IGDdatas * data)
  81. {
  82. struct xmlparser parser;
  83. /* xmlparser object */
  84. parser.xmlstart = buffer;
  85. parser.xmlsize = bufsize;
  86. parser.data = data;
  87. parser.starteltfunc = IGDstartelt;
  88. parser.endeltfunc = IGDendelt;
  89. parser.datafunc = IGDdata;
  90. parser.attfunc = 0;
  91. parsexml(&parser);
  92. #ifdef DEBUG
  93. printIGD(data);
  94. #endif
  95. }
  96. #if 0
  97. /* getcontentlenfromline() : parse the Content-Length HTTP header line.
  98. * Content-length: nnn */
  99. static int getcontentlenfromline(const char * p, int n)
  100. {
  101. static const char contlenstr[] = "content-length";
  102. const char * p2 = contlenstr;
  103. int a = 0;
  104. while(*p2)
  105. {
  106. if(n==0)
  107. return -1;
  108. if(*p2 != *p && *p2 != (*p + 32))
  109. return -1;
  110. p++; p2++; n--;
  111. }
  112. if(n==0)
  113. return -1;
  114. if(*p != ':')
  115. return -1;
  116. p++; n--;
  117. while(*p == ' ')
  118. {
  119. if(n==0)
  120. return -1;
  121. p++; n--;
  122. }
  123. while(*p >= '0' && *p <= '9')
  124. {
  125. if(n==0)
  126. return -1;
  127. a = (a * 10) + (*p - '0');
  128. p++; n--;
  129. }
  130. return a;
  131. }
  132. /* getContentLengthAndHeaderLength()
  133. * retrieve header length and content length from an HTTP response
  134. * TODO : retrieve Transfer-Encoding: header value, in order to support
  135. * HTTP/1.1, chunked transfer encoding must be supported. */
  136. static void
  137. getContentLengthAndHeaderLength(char * p, int n,
  138. int * contentlen, int * headerlen)
  139. {
  140. char * line;
  141. int linelen;
  142. int r;
  143. line = p;
  144. while(line < p + n)
  145. {
  146. linelen = 0;
  147. while(line[linelen] != '\r' && line[linelen] != '\r')
  148. {
  149. if(line+linelen >= p+n)
  150. return;
  151. linelen++;
  152. }
  153. r = getcontentlenfromline(line, linelen);
  154. if(r>0)
  155. *contentlen = r;
  156. line = line + linelen + 2;
  157. if(line[0] == '\r' && line[1] == '\n')
  158. {
  159. *headerlen = (line - p) + 2;
  160. return;
  161. }
  162. }
  163. }
  164. #endif
  165. /* simpleUPnPcommand2 :
  166. * not so simple !
  167. * return values :
  168. * 0 - OK
  169. * -1 - error */
  170. static int simpleUPnPcommand2(int s, const char * url, const char * service,
  171. const char * action, struct UPNParg * args,
  172. char * buffer, int * bufsize, const char * httpversion)
  173. {
  174. char hostname[MAXHOSTNAMELEN+1];
  175. unsigned short port = 0;
  176. char * path;
  177. char soapact[128];
  178. char soapbody[2048];
  179. char * buf;
  180. /*int buffree;*/
  181. int n;
  182. /*int contentlen, headerlen;*/ /* for the response */
  183. snprintf(soapact, sizeof(soapact), "%s#%s", service, action);
  184. if(args==NULL)
  185. {
  186. /*soapbodylen = */snprintf(soapbody, sizeof(soapbody),
  187. "<?xml version=\"1.0\"?>\r\n"
  188. "<" SOAPPREFIX ":Envelope "
  189. "xmlns:" SOAPPREFIX "=\"http://schemas.xmlsoap.org/soap/envelope/\" "
  190. SOAPPREFIX ":encodingStyle=\"http://schemas.xmlsoap.org/soap/encoding/\">"
  191. "<" SOAPPREFIX ":Body>"
  192. "<" SERVICEPREFIX ":%s xmlns:" SERVICEPREFIX "=\"%s\">"
  193. "</" SERVICEPREFIX ":%s>"
  194. "</" SOAPPREFIX ":Body></" SOAPPREFIX ":Envelope>"
  195. "\r\n", action, service, action);
  196. }
  197. else
  198. {
  199. char * p;
  200. const char * pe, * pv;
  201. int soapbodylen;
  202. soapbodylen = snprintf(soapbody, sizeof(soapbody),
  203. "<?xml version=\"1.0\"?>\r\n"
  204. "<" SOAPPREFIX ":Envelope "
  205. "xmlns:" SOAPPREFIX "=\"http://schemas.xmlsoap.org/soap/envelope/\" "
  206. SOAPPREFIX ":encodingStyle=\"http://schemas.xmlsoap.org/soap/encoding/\">"
  207. "<" SOAPPREFIX ":Body>"
  208. "<" SERVICEPREFIX ":%s xmlns:" SERVICEPREFIX "=\"%s\">",
  209. action, service);
  210. p = soapbody + soapbodylen;
  211. while(args->elt)
  212. {
  213. /* check that we are never overflowing the string... */
  214. if(soapbody + sizeof(soapbody) <= p + 100)
  215. {
  216. /* we keep a margin of at least 100 bytes */
  217. *bufsize = 0;
  218. return -1;
  219. }
  220. *(p++) = '<';
  221. pe = args->elt;
  222. while(*pe)
  223. *(p++) = *(pe++);
  224. *(p++) = '>';
  225. if((pv = args->val))
  226. {
  227. while(*pv)
  228. *(p++) = *(pv++);
  229. }
  230. *(p++) = '<';
  231. *(p++) = '/';
  232. pe = args->elt;
  233. while(*pe)
  234. *(p++) = *(pe++);
  235. *(p++) = '>';
  236. args++;
  237. }
  238. *(p++) = '<';
  239. *(p++) = '/';
  240. *(p++) = SERVICEPREFIX2;
  241. *(p++) = ':';
  242. pe = action;
  243. while(*pe)
  244. *(p++) = *(pe++);
  245. strncpy(p, "></" SOAPPREFIX ":Body></" SOAPPREFIX ":Envelope>\r\n",
  246. soapbody + sizeof(soapbody) - p);
  247. }
  248. if(!parseURL(url, hostname, &port, &path)) return -1;
  249. if(s<0)
  250. {
  251. s = connecthostport(hostname, port);
  252. if(s < 0)
  253. {
  254. *bufsize = 0;
  255. return -1;
  256. }
  257. }
  258. n = soapPostSubmit(s, path, hostname, port, soapact, soapbody, httpversion);
  259. if(n<=0) {
  260. #ifdef DEBUG
  261. printf("Error sending SOAP request\n");
  262. #endif
  263. closesocket(s);
  264. return -1;
  265. }
  266. #if 0
  267. contentlen = -1;
  268. headerlen = -1;
  269. buf = buffer;
  270. buffree = *bufsize;
  271. *bufsize = 0;
  272. while ((n = ReceiveData(s, buf, buffree, 5000)) > 0) {
  273. buffree -= n;
  274. buf += n;
  275. *bufsize += n;
  276. getContentLengthAndHeaderLength(buffer, *bufsize,
  277. &contentlen, &headerlen);
  278. #ifdef DEBUG
  279. printf("received n=%dbytes bufsize=%d ContLen=%d HeadLen=%d\n",
  280. n, *bufsize, contentlen, headerlen);
  281. #endif
  282. /* break if we received everything */
  283. if(contentlen > 0 && headerlen > 0 && *bufsize >= contentlen+headerlen)
  284. break;
  285. }
  286. #endif
  287. buf = getHTTPResponse(s, &n);
  288. if(n > 0 && buf)
  289. {
  290. #ifdef DEBUG
  291. printf("SOAP Response :\n%.*s\n", n, buf);
  292. #endif
  293. if(*bufsize > n)
  294. {
  295. memcpy(buffer, buf, n);
  296. *bufsize = n;
  297. }
  298. else
  299. {
  300. memcpy(buffer, buf, *bufsize);
  301. }
  302. free(buf);
  303. buf = 0;
  304. }
  305. closesocket(s);
  306. return 0;
  307. }
  308. /* simpleUPnPcommand :
  309. * not so simple !
  310. * return values :
  311. * 0 - OK
  312. * -1 - error */
  313. int simpleUPnPcommand(int s, const char * url, const char * service,
  314. const char * action, struct UPNParg * args,
  315. char * buffer, int * bufsize)
  316. {
  317. int result;
  318. /*int origbufsize = *bufsize;*/
  319. result = simpleUPnPcommand2(s, url, service, action, args, buffer, bufsize, "1.1");
  320. /*
  321. result = simpleUPnPcommand2(s, url, service, action, args, buffer, bufsize, "1.0");
  322. if (result < 0 || *bufsize == 0)
  323. {
  324. #if DEBUG
  325. printf("Error or no result from SOAP request; retrying with HTTP/1.1\n");
  326. #endif
  327. *bufsize = origbufsize;
  328. result = simpleUPnPcommand2(s, url, service, action, args, buffer, bufsize, "1.1");
  329. }
  330. */
  331. return result;
  332. }
  333. /* parseMSEARCHReply()
  334. * the last 4 arguments are filled during the parsing :
  335. * - location/locationsize : "location:" field of the SSDP reply packet
  336. * - st/stsize : "st:" field of the SSDP reply packet.
  337. * The strings are NOT null terminated */
  338. static void
  339. parseMSEARCHReply(const char * reply, int size,
  340. const char * * location, int * locationsize,
  341. const char * * st, int * stsize)
  342. {
  343. int a, b, i;
  344. i = 0;
  345. a = i; /* start of the line */
  346. b = 0;
  347. while(i<size)
  348. {
  349. switch(reply[i])
  350. {
  351. case ':':
  352. if(b==0)
  353. {
  354. b = i; /* end of the "header" */
  355. /*for(j=a; j<b; j++)
  356. {
  357. putchar(reply[j]);
  358. }
  359. */
  360. }
  361. break;
  362. case '\x0a':
  363. case '\x0d':
  364. if(b!=0)
  365. {
  366. /*for(j=b+1; j<i; j++)
  367. {
  368. putchar(reply[j]);
  369. }
  370. putchar('\n');*/
  371. do { b++; } while(reply[b]==' ');
  372. if(0==strncasecmp(reply+a, "location", 8))
  373. {
  374. *location = reply+b;
  375. *locationsize = i-b;
  376. }
  377. else if(0==strncasecmp(reply+a, "st", 2))
  378. {
  379. *st = reply+b;
  380. *stsize = i-b;
  381. }
  382. b = 0;
  383. }
  384. a = i+1;
  385. break;
  386. default:
  387. break;
  388. }
  389. i++;
  390. }
  391. }
  392. /* port upnp discover : SSDP protocol */
  393. #define PORT 1900
  394. #define XSTR(s) STR(s)
  395. #define STR(s) #s
  396. #define UPNP_MCAST_ADDR "239.255.255.250"
  397. /* upnpDiscover() :
  398. * return a chained list of all devices found or NULL if
  399. * no devices was found.
  400. * It is up to the caller to free the chained list
  401. * delay is in millisecond (poll) */
  402. LIBSPEC struct UPNPDev * upnpDiscover(int delay, const char * multicastif,
  403. const char * minissdpdsock, int sameport)
  404. {
  405. struct UPNPDev * tmp;
  406. struct UPNPDev * devlist = 0;
  407. int opt = 1;
  408. static const char MSearchMsgFmt[] =
  409. "M-SEARCH * HTTP/1.1\r\n"
  410. "HOST: " UPNP_MCAST_ADDR ":" XSTR(PORT) "\r\n"
  411. "ST: %s\r\n"
  412. "MAN: \"ssdp:discover\"\r\n"
  413. "MX: %u\r\n"
  414. "\r\n";
  415. static const char * const deviceList[] = {
  416. "urn:schemas-upnp-org:device:InternetGatewayDevice:1",
  417. "urn:schemas-upnp-org:service:WANIPConnection:1",
  418. "urn:schemas-upnp-org:service:WANPPPConnection:1",
  419. "upnp:rootdevice",
  420. 0
  421. };
  422. int deviceIndex = 0;
  423. char bufr[1536]; /* reception and emission buffer */
  424. int sudp;
  425. int n;
  426. struct sockaddr sockudp_r;
  427. unsigned int mx;
  428. #ifdef NO_GETADDRINFO
  429. struct sockaddr_in sockudp_w;
  430. #else
  431. int rv;
  432. struct addrinfo hints, *servinfo, *p;
  433. #endif
  434. #ifdef WIN32
  435. MIB_IPFORWARDROW ip_forward;
  436. #endif
  437. #if !defined(WIN32) && !defined(__amigaos__) && !defined(__amigaos4__)
  438. /* first try to get infos from minissdpd ! */
  439. if(!minissdpdsock)
  440. minissdpdsock = "/var/run/minissdpd.sock";
  441. while(!devlist && deviceList[deviceIndex]) {
  442. devlist = getDevicesFromMiniSSDPD(deviceList[deviceIndex],
  443. minissdpdsock);
  444. /* We return what we have found if it was not only a rootdevice */
  445. if(devlist && !strstr(deviceList[deviceIndex], "rootdevice"))
  446. return devlist;
  447. deviceIndex++;
  448. }
  449. deviceIndex = 0;
  450. #endif
  451. /* fallback to direct discovery */
  452. #ifdef WIN32
  453. sudp = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP);
  454. #else
  455. sudp = socket(PF_INET, SOCK_DGRAM, 0);
  456. #endif
  457. if(sudp < 0)
  458. {
  459. PRINT_SOCKET_ERROR("socket");
  460. return NULL;
  461. }
  462. /* reception */
  463. memset(&sockudp_r, 0, sizeof(struct sockaddr));
  464. if(0/*ipv6*/) {
  465. struct sockaddr_in6 * p = (struct sockaddr_in6 *)&sockudp_r;
  466. p->sin6_family = AF_INET6;
  467. if(sameport)
  468. p->sin6_port = htons(PORT);
  469. p->sin6_addr = in6addr_any;//IN6ADDR_ANY_INIT;/*INADDR_ANY;*/
  470. } else {
  471. struct sockaddr_in * p = (struct sockaddr_in *)&sockudp_r;
  472. p->sin_family = AF_INET;
  473. if(sameport)
  474. p->sin_port = htons(PORT);
  475. p->sin_addr.s_addr = INADDR_ANY;
  476. }
  477. #ifdef WIN32
  478. /* This code could help us to use the right Network interface for
  479. * SSDP multicast traffic */
  480. /* Get IP associated with the index given in the ip_forward struct
  481. * in order to give this ip to setsockopt(sudp, IPPROTO_IP, IP_MULTICAST_IF) */
  482. if(GetBestRoute(inet_addr("223.255.255.255"), 0, &ip_forward) == NO_ERROR) {
  483. DWORD dwRetVal = 0;
  484. PMIB_IPADDRTABLE pIPAddrTable;
  485. DWORD dwSize = 0;
  486. #ifdef DEBUG
  487. IN_ADDR IPAddr;
  488. #endif
  489. int i;
  490. #ifdef DEBUG
  491. printf("ifIndex=%lu nextHop=%lx \n", ip_forward.dwForwardIfIndex, ip_forward.dwForwardNextHop);
  492. #endif
  493. pIPAddrTable = (MIB_IPADDRTABLE *) malloc(sizeof (MIB_IPADDRTABLE));
  494. if (GetIpAddrTable(pIPAddrTable, &dwSize, 0) == ERROR_INSUFFICIENT_BUFFER) {
  495. free(pIPAddrTable);
  496. pIPAddrTable = (MIB_IPADDRTABLE *) malloc(dwSize);
  497. }
  498. if(pIPAddrTable) {
  499. dwRetVal = GetIpAddrTable( pIPAddrTable, &dwSize, 0 );
  500. #ifdef DEBUG
  501. printf("\tNum Entries: %ld\n", pIPAddrTable->dwNumEntries);
  502. #endif
  503. for (i=0; i < (int) pIPAddrTable->dwNumEntries; i++) {
  504. #ifdef DEBUG
  505. printf("\n\tInterface Index[%d]:\t%ld\n", i, pIPAddrTable->table[i].dwIndex);
  506. IPAddr.S_un.S_addr = (u_long) pIPAddrTable->table[i].dwAddr;
  507. printf("\tIP Address[%d]: \t%s\n", i, inet_ntoa(IPAddr) );
  508. IPAddr.S_un.S_addr = (u_long) pIPAddrTable->table[i].dwMask;
  509. printf("\tSubnet Mask[%d]: \t%s\n", i, inet_ntoa(IPAddr) );
  510. IPAddr.S_un.S_addr = (u_long) pIPAddrTable->table[i].dwBCastAddr;
  511. printf("\tBroadCast[%d]: \t%s (%ld)\n", i, inet_ntoa(IPAddr), pIPAddrTable->table[i].dwBCastAddr);
  512. printf("\tReassembly size[%d]:\t%ld\n", i, pIPAddrTable->table[i].dwReasmSize);
  513. printf("\tType and State[%d]:", i);
  514. printf("\n");
  515. #endif
  516. if (pIPAddrTable->table[i].dwIndex == ip_forward.dwForwardIfIndex) {
  517. /* Set the address of this interface to be used */
  518. struct in_addr mc_if;
  519. memset(&mc_if, 0, sizeof(mc_if));
  520. mc_if.s_addr = pIPAddrTable->table[i].dwAddr;
  521. if(setsockopt(sudp, IPPROTO_IP, IP_MULTICAST_IF, (const char *)&mc_if, sizeof(mc_if)) < 0) {
  522. PRINT_SOCKET_ERROR("setsockopt");
  523. }
  524. ((struct sockaddr_in *)&sockudp_r)->sin_addr.s_addr = pIPAddrTable->table[i].dwAddr;
  525. #ifndef DEBUG
  526. break;
  527. #endif
  528. }
  529. }
  530. free(pIPAddrTable);
  531. pIPAddrTable = NULL;
  532. }
  533. }
  534. #endif
  535. #ifdef WIN32
  536. if (setsockopt(sudp, SOL_SOCKET, SO_REUSEADDR, (const char *)&opt, sizeof (opt)) < 0)
  537. #else
  538. if (setsockopt(sudp, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof (opt)) < 0)
  539. #endif
  540. {
  541. PRINT_SOCKET_ERROR("setsockopt");
  542. return NULL;
  543. }
  544. if(multicastif)
  545. {
  546. struct in_addr mc_if;
  547. mc_if.s_addr = inet_addr(multicastif);
  548. if(0/*ipv6*/) {
  549. } else {
  550. ((struct sockaddr_in *)&sockudp_r)->sin_addr.s_addr = mc_if.s_addr;
  551. }
  552. if(setsockopt(sudp, IPPROTO_IP, IP_MULTICAST_IF, (const char *)&mc_if, sizeof(mc_if)) < 0)
  553. {
  554. PRINT_SOCKET_ERROR("setsockopt");
  555. }
  556. }
  557. /* Avant d'envoyer le paquet on bind pour recevoir la reponse */
  558. if (bind(sudp, &sockudp_r, 0/*ipv6*/?sizeof(struct sockaddr_in6):sizeof(struct sockaddr_in)) != 0)
  559. {
  560. PRINT_SOCKET_ERROR("bind");
  561. closesocket(sudp);
  562. return NULL;
  563. }
  564. /* Calculating maximum response time in seconds */
  565. mx = ((unsigned int)delay) / 1000u;
  566. /* receiving SSDP response packet */
  567. for(n = 0;;)
  568. {
  569. if(n == 0)
  570. {
  571. /* sending the SSDP M-SEARCH packet */
  572. n = snprintf(bufr, sizeof(bufr),
  573. MSearchMsgFmt, deviceList[deviceIndex++], mx);
  574. /*printf("Sending %s", bufr);*/
  575. #ifdef NO_GETADDRINFO
  576. /* the following code is not using getaddrinfo */
  577. /* emission */
  578. memset(&sockudp_w, 0, sizeof(struct sockaddr_in));
  579. sockudp_w.sin_family = AF_INET;
  580. sockudp_w.sin_port = htons(PORT);
  581. sockudp_w.sin_addr.s_addr = inet_addr(UPNP_MCAST_ADDR);
  582. n = sendto(sudp, bufr, n, 0,
  583. (struct sockaddr *)&sockudp_w, sizeof(struct sockaddr_in));
  584. if (n < 0) {
  585. PRINT_SOCKET_ERROR("sendto");
  586. closesocket(sudp);
  587. return devlist;
  588. }
  589. #else /* #ifdef NO_GETADDRINFO */
  590. memset(&hints, 0, sizeof(hints));
  591. hints.ai_family = AF_UNSPEC; // AF_INET6 or AF_INET
  592. hints.ai_socktype = SOCK_DGRAM;
  593. /*hints.ai_flags = */
  594. if ((rv = getaddrinfo(UPNP_MCAST_ADDR, XSTR(PORT), &hints, &servinfo)) != 0) {
  595. #ifdef WIN32
  596. fprintf(stderr, "getaddrinfo() failed: %d\n", rv);
  597. #else
  598. fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(rv));
  599. #endif
  600. return devlist;
  601. }
  602. for(p = servinfo; p; p = p->ai_next) {
  603. n = sendto(sudp, bufr, n, 0, p->ai_addr, p->ai_addrlen);
  604. if (n < 0) {
  605. PRINT_SOCKET_ERROR("sendto");
  606. continue;
  607. }
  608. }
  609. freeaddrinfo(servinfo);
  610. if(n < 0) {
  611. closesocket(sudp);
  612. return devlist;
  613. }
  614. #endif /* #ifdef NO_GETADDRINFO */
  615. }
  616. /* Waiting for SSDP REPLY packet to M-SEARCH */
  617. n = ReceiveData(sudp, bufr, sizeof(bufr), delay);
  618. if (n < 0) {
  619. /* error */
  620. closesocket(sudp);
  621. return devlist;
  622. } else if (n == 0) {
  623. /* no data or Time Out */
  624. if (devlist || (deviceList[deviceIndex] == 0)) {
  625. /* no more device type to look for... */
  626. closesocket(sudp);
  627. return devlist;
  628. }
  629. } else {
  630. const char * descURL=NULL;
  631. int urlsize=0;
  632. const char * st=NULL;
  633. int stsize=0;
  634. /*printf("%d byte(s) :\n%s\n", n, bufr);*/ /* affichage du message */
  635. parseMSEARCHReply(bufr, n, &descURL, &urlsize, &st, &stsize);
  636. if(st&&descURL)
  637. {
  638. #ifdef DEBUG
  639. printf("M-SEARCH Reply:\nST: %.*s\nLocation: %.*s\n",
  640. stsize, st, urlsize, descURL);
  641. #endif
  642. for(tmp=devlist; tmp; tmp = tmp->pNext) {
  643. if(memcmp(tmp->descURL, descURL, urlsize) == 0 &&
  644. tmp->descURL[urlsize] == '\0' &&
  645. memcmp(tmp->st, st, stsize) == 0 &&
  646. tmp->st[stsize] == '\0')
  647. break;
  648. }
  649. /* at the exit of the loop above, tmp is null if
  650. * no duplicate device was found */
  651. if(tmp)
  652. continue;
  653. tmp = (struct UPNPDev *)malloc(sizeof(struct UPNPDev)+urlsize+stsize);
  654. tmp->pNext = devlist;
  655. tmp->descURL = tmp->buffer;
  656. tmp->st = tmp->buffer + 1 + urlsize;
  657. memcpy(tmp->buffer, descURL, urlsize);
  658. tmp->buffer[urlsize] = '\0';
  659. memcpy(tmp->buffer + urlsize + 1, st, stsize);
  660. tmp->buffer[urlsize+1+stsize] = '\0';
  661. devlist = tmp;
  662. }
  663. }
  664. }
  665. }
  666. /* freeUPNPDevlist() should be used to
  667. * free the chained list returned by upnpDiscover() */
  668. LIBSPEC void freeUPNPDevlist(struct UPNPDev * devlist)
  669. {
  670. struct UPNPDev * next;
  671. while(devlist)
  672. {
  673. next = devlist->pNext;
  674. free(devlist);
  675. devlist = next;
  676. }
  677. }
  678. static void
  679. url_cpy_or_cat(char * dst, const char * src, int n)
  680. {
  681. if( (src[0] == 'h')
  682. &&(src[1] == 't')
  683. &&(src[2] == 't')
  684. &&(src[3] == 'p')
  685. &&(src[4] == ':')
  686. &&(src[5] == '/')
  687. &&(src[6] == '/'))
  688. {
  689. strncpy(dst, src, n);
  690. }
  691. else
  692. {
  693. int l = strlen(dst);
  694. if(src[0] != '/')
  695. dst[l++] = '/';
  696. if(l<=n)
  697. strncpy(dst + l, src, n - l);
  698. }
  699. }
  700. /* Prepare the Urls for usage...
  701. */
  702. LIBSPEC void GetUPNPUrls(struct UPNPUrls * urls, struct IGDdatas * data,
  703. const char * descURL)
  704. {
  705. char * p;
  706. int n1, n2, n3;
  707. n1 = strlen(data->urlbase);
  708. if(n1==0)
  709. n1 = strlen(descURL);
  710. n1 += 2; /* 1 byte more for Null terminator, 1 byte for '/' if needed */
  711. n2 = n1; n3 = n1;
  712. n1 += strlen(data->first.scpdurl);
  713. n2 += strlen(data->first.controlurl);
  714. n3 += strlen(data->CIF.controlurl);
  715. urls->ipcondescURL = (char *)malloc(n1);
  716. urls->controlURL = (char *)malloc(n2);
  717. urls->controlURL_CIF = (char *)malloc(n3);
  718. /* maintenant on chope la desc du WANIPConnection */
  719. if(data->urlbase[0] != '\0')
  720. strncpy(urls->ipcondescURL, data->urlbase, n1);
  721. else
  722. strncpy(urls->ipcondescURL, descURL, n1);
  723. p = strchr(urls->ipcondescURL+7, '/');
  724. if(p) p[0] = '\0';
  725. strncpy(urls->controlURL, urls->ipcondescURL, n2);
  726. strncpy(urls->controlURL_CIF, urls->ipcondescURL, n3);
  727. url_cpy_or_cat(urls->ipcondescURL, data->first.scpdurl, n1);
  728. url_cpy_or_cat(urls->controlURL, data->first.controlurl, n2);
  729. url_cpy_or_cat(urls->controlURL_CIF, data->CIF.controlurl, n3);
  730. #ifdef DEBUG
  731. printf("urls->ipcondescURL='%s' %u n1=%d\n", urls->ipcondescURL,
  732. (unsigned)strlen(urls->ipcondescURL), n1);
  733. printf("urls->controlURL='%s' %u n2=%d\n", urls->controlURL,
  734. (unsigned)strlen(urls->controlURL), n2);
  735. printf("urls->controlURL_CIF='%s' %u n3=%d\n", urls->controlURL_CIF,
  736. (unsigned)strlen(urls->controlURL_CIF), n3);
  737. #endif
  738. }
  739. LIBSPEC void
  740. FreeUPNPUrls(struct UPNPUrls * urls)
  741. {
  742. if(!urls)
  743. return;
  744. free(urls->controlURL);
  745. urls->controlURL = 0;
  746. free(urls->ipcondescURL);
  747. urls->ipcondescURL = 0;
  748. free(urls->controlURL_CIF);
  749. urls->controlURL_CIF = 0;
  750. }
  751. int ReceiveData(int socket, char * data, int length, int timeout)
  752. {
  753. int n;
  754. #if !defined(WIN32) && !defined(__amigaos__) && !defined(__amigaos4__)
  755. struct pollfd fds[1]; /* for the poll */
  756. #ifdef MINIUPNPC_IGNORE_EINTR
  757. do {
  758. #endif
  759. fds[0].fd = socket;
  760. fds[0].events = POLLIN;
  761. n = poll(fds, 1, timeout);
  762. #ifdef MINIUPNPC_IGNORE_EINTR
  763. } while(n < 0 && errno == EINTR);
  764. #endif
  765. if(n < 0)
  766. {
  767. PRINT_SOCKET_ERROR("poll");
  768. return -1;
  769. }
  770. else if(n == 0)
  771. {
  772. return 0;
  773. }
  774. #else
  775. fd_set socketSet;
  776. TIMEVAL timeval;
  777. FD_ZERO(&socketSet);
  778. FD_SET(socket, &socketSet);
  779. timeval.tv_sec = timeout / 1000;
  780. timeval.tv_usec = (timeout % 1000) * 1000;
  781. n = select(FD_SETSIZE, &socketSet, NULL, NULL, &timeval);
  782. if(n < 0)
  783. {
  784. PRINT_SOCKET_ERROR("select");
  785. return -1;
  786. }
  787. else if(n == 0)
  788. {
  789. return 0;
  790. }
  791. #endif
  792. n = recv(socket, data, length, 0);
  793. if(n<0)
  794. {
  795. PRINT_SOCKET_ERROR("recv");
  796. }
  797. return n;
  798. }
  799. int
  800. UPNPIGD_IsConnected(struct UPNPUrls * urls, struct IGDdatas * data)
  801. {
  802. char status[64];
  803. unsigned int uptime;
  804. status[0] = '\0';
  805. UPNP_GetStatusInfo(urls->controlURL, data->first.servicetype,
  806. status, &uptime, NULL);
  807. if(0 == strcmp("Connected", status))
  808. {
  809. return 1;
  810. }
  811. else
  812. return 0;
  813. }
  814. /* UPNP_GetValidIGD() :
  815. * return values :
  816. * 0 = NO IGD found
  817. * 1 = A valid connected IGD has been found
  818. * 2 = A valid IGD has been found but it reported as
  819. * not connected
  820. * 3 = an UPnP device has been found but was not recognized as an IGD
  821. *
  822. * In any non zero return case, the urls and data structures
  823. * passed as parameters are set. Donc forget to call FreeUPNPUrls(urls) to
  824. * free allocated memory.
  825. */
  826. LIBSPEC int
  827. UPNP_GetValidIGD(struct UPNPDev * devlist,
  828. struct UPNPUrls * urls,
  829. struct IGDdatas * data,
  830. char * lanaddr, int lanaddrlen)
  831. {
  832. char * descXML;
  833. int descXMLsize = 0;
  834. struct UPNPDev * dev;
  835. int ndev = 0;
  836. int state; /* state 1 : IGD connected. State 2 : IGD. State 3 : anything */
  837. if(!devlist)
  838. {
  839. #ifdef DEBUG
  840. printf("Empty devlist\n");
  841. #endif
  842. return 0;
  843. }
  844. for(state = 1; state <= 3; state++)
  845. {
  846. for(dev = devlist; dev; dev = dev->pNext)
  847. {
  848. /* we should choose an internet gateway device.
  849. * with st == urn:schemas-upnp-org:device:InternetGatewayDevice:1 */
  850. descXML = miniwget_getaddr(dev->descURL, &descXMLsize,
  851. lanaddr, lanaddrlen);
  852. if(descXML)
  853. {
  854. ndev++;
  855. memset(data, 0, sizeof(struct IGDdatas));
  856. memset(urls, 0, sizeof(struct UPNPUrls));
  857. parserootdesc(descXML, descXMLsize, data);
  858. free(descXML);
  859. descXML = NULL;
  860. if(0==strcmp(data->CIF.servicetype,
  861. "urn:schemas-upnp-org:service:WANCommonInterfaceConfig:1")
  862. || state >= 3 )
  863. {
  864. GetUPNPUrls(urls, data, dev->descURL);
  865. #ifdef DEBUG
  866. printf("UPNPIGD_IsConnected(%s) = %d\n",
  867. urls->controlURL,
  868. UPNPIGD_IsConnected(urls, data));
  869. #endif
  870. if((state >= 2) || UPNPIGD_IsConnected(urls, data))
  871. return state;
  872. FreeUPNPUrls(urls);
  873. if(data->second.servicetype[0] != '\0') {
  874. #ifdef DEBUG
  875. printf("We tried %s, now we try %s !\n",
  876. data->first.servicetype, data->second.servicetype);
  877. #endif
  878. /* swaping WANPPPConnection and WANIPConnection ! */
  879. memcpy(&data->tmp, &data->first, sizeof(struct IGDdatas_service));
  880. memcpy(&data->first, &data->second, sizeof(struct IGDdatas_service));
  881. memcpy(&data->second, &data->tmp, sizeof(struct IGDdatas_service));
  882. GetUPNPUrls(urls, data, dev->descURL);
  883. #ifdef DEBUG
  884. printf("UPNPIGD_IsConnected(%s) = %d\n",
  885. urls->controlURL,
  886. UPNPIGD_IsConnected(urls, data));
  887. #endif
  888. if((state >= 2) || UPNPIGD_IsConnected(urls, data))
  889. return state;
  890. FreeUPNPUrls(urls);
  891. }
  892. }
  893. memset(data, 0, sizeof(struct IGDdatas));
  894. }
  895. #ifdef DEBUG
  896. else
  897. {
  898. printf("error getting XML description %s\n", dev->descURL);
  899. }
  900. #endif
  901. }
  902. }
  903. return 0;
  904. }
  905. /* UPNP_GetIGDFromUrl()
  906. * Used when skipping the discovery process.
  907. * return value :
  908. * 0 - Not ok
  909. * 1 - OK */
  910. int
  911. UPNP_GetIGDFromUrl(const char * rootdescurl,
  912. struct UPNPUrls * urls,
  913. struct IGDdatas * data,
  914. char * lanaddr, int lanaddrlen)
  915. {
  916. char * descXML;
  917. int descXMLsize = 0;
  918. descXML = miniwget_getaddr(rootdescurl, &descXMLsize,
  919. lanaddr, lanaddrlen);
  920. if(descXML) {
  921. memset(data, 0, sizeof(struct IGDdatas));
  922. memset(urls, 0, sizeof(struct UPNPUrls));
  923. parserootdesc(descXML, descXMLsize, data);
  924. free(descXML);
  925. descXML = NULL;
  926. GetUPNPUrls(urls, data, rootdescurl);
  927. return 1;
  928. } else {
  929. return 0;
  930. }
  931. }
粤ICP备19079148号