mupnp 0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,38 @@
1
+ /* $Id: igd_desc_parse.h,v 1.5 2007/04/11 15:21:09 nanard Exp $ */
2
+ /* Project : miniupnp
3
+ * http://miniupnp.free.fr/
4
+ * Author : Thomas Bernard
5
+ * Copyright (c) 2005 Thomas Bernard
6
+ * This software is subject to the conditions detailed in the
7
+ * LICENCE file provided in this distribution.
8
+ * */
9
+ #ifndef __IGD_DESC_PARSE_H__
10
+ #define __IGD_DESC_PARSE_H__
11
+
12
+ /* Structure to store the result of the parsing of UPnP
13
+ * descriptions of Internet Gateway Devices */
14
+ #define MINIUPNPC_URL_MAXSIZE (128)
15
+ struct IGDdatas {
16
+ char cureltname[MINIUPNPC_URL_MAXSIZE];
17
+ char urlbase[MINIUPNPC_URL_MAXSIZE];
18
+ int level;
19
+ int state;
20
+ char controlurl_CIF[MINIUPNPC_URL_MAXSIZE];
21
+ char eventsuburl_CIF[MINIUPNPC_URL_MAXSIZE];
22
+ char scpdurl_CIF[MINIUPNPC_URL_MAXSIZE];
23
+ char servicetype_CIF[MINIUPNPC_URL_MAXSIZE];
24
+ char devicetype_CIF[MINIUPNPC_URL_MAXSIZE];
25
+ char controlurl[MINIUPNPC_URL_MAXSIZE];
26
+ char eventsuburl[MINIUPNPC_URL_MAXSIZE];
27
+ char scpdurl[MINIUPNPC_URL_MAXSIZE];
28
+ char servicetype[MINIUPNPC_URL_MAXSIZE];
29
+ char devicetype[MINIUPNPC_URL_MAXSIZE];
30
+ };
31
+
32
+ void IGDstartelt(void *, const char *, int);
33
+ void IGDendelt(void *, const char *, int);
34
+ void IGDdata(void *, const char *, int);
35
+ void printIGD(struct IGDdatas *);
36
+
37
+ #endif
38
+
data/ext/minisoap.c ADDED
@@ -0,0 +1,112 @@
1
+ /* $Id: minisoap.c,v 1.15 2008/02/17 17:57:07 nanard Exp $ */
2
+ /* Project : miniupnp
3
+ * Author : Thomas Bernard
4
+ * Copyright (c) 2005 Thomas Bernard
5
+ * This software is subject to the conditions detailed in the
6
+ * LICENCE file provided in this distribution.
7
+ *
8
+ * Minimal SOAP implementation for UPnP protocol.
9
+ */
10
+ #include <stdio.h>
11
+ #include <string.h>
12
+ #ifdef WIN32
13
+ #include <io.h>
14
+ #include <winsock2.h>
15
+ #define snprintf _snprintf
16
+ #else
17
+ #include <unistd.h>
18
+ #include <sys/types.h>
19
+ #include <sys/socket.h>
20
+ #endif
21
+ #include "minisoap.h"
22
+
23
+ /* only for malloc */
24
+ #include <stdlib.h>
25
+
26
+ #ifdef WIN32
27
+ #define PRINT_SOCKET_ERROR(x) printf("Socket error: %s, %d\n", x, WSAGetLastError());
28
+ #else
29
+ #define PRINT_SOCKET_ERROR(x) perror(x)
30
+ #endif
31
+
32
+ /* httpWrite sends the headers and the body to the socket
33
+ * and returns the number of bytes sent */
34
+ static int
35
+ httpWrite(int fd, const char * body, int bodysize,
36
+ const char * headers, int headerssize)
37
+ {
38
+ int n = 0;
39
+ /*n = write(fd, headers, headerssize);*/
40
+ /*if(bodysize>0)
41
+ n += write(fd, body, bodysize);*/
42
+ /* Note : my old linksys router only took into account
43
+ * soap request that are sent into only one packet */
44
+ char * p;
45
+ /* TODO: AVOID MALLOC */
46
+ p = malloc(headerssize+bodysize);
47
+ if(!p)
48
+ return 0;
49
+ memcpy(p, headers, headerssize);
50
+ memcpy(p+headerssize, body, bodysize);
51
+ /*n = write(fd, p, headerssize+bodysize);*/
52
+ n = send(fd, p, headerssize+bodysize, 0);
53
+ if(n<0) {
54
+ PRINT_SOCKET_ERROR("send");
55
+ }
56
+ /* disable send on the socket */
57
+ /* draytek routers dont seems to like that... */
58
+ #if 0
59
+ #ifdef WIN32
60
+ if(shutdown(fd, SD_SEND)<0) {
61
+ #else
62
+ if(shutdown(fd, SHUT_WR)<0) { /*SD_SEND*/
63
+ #endif
64
+ PRINT_SOCKET_ERROR("shutdown");
65
+ }
66
+ #endif
67
+ free(p);
68
+ return n;
69
+ }
70
+
71
+ /* self explanatory */
72
+ int soapPostSubmit(int fd,
73
+ const char * url,
74
+ const char * host,
75
+ unsigned short port,
76
+ const char * action,
77
+ const char * body)
78
+ {
79
+ int bodysize;
80
+ char headerbuf[512];
81
+ int headerssize;
82
+ char portstr[8];
83
+ bodysize = (int)strlen(body);
84
+ /* We are not using keep-alive HTTP connections.
85
+ * HTTP/1.1 needs the header Connection: close to do that.
86
+ * This is the default with HTTP/1.0 */
87
+ /* Connection: Close is normally there only in HTTP/1.1 but who knows */
88
+ portstr[0] = '\0';
89
+ if(port != 80)
90
+ snprintf(portstr, sizeof(portstr), ":%hu", port);
91
+ headerssize = snprintf(headerbuf, sizeof(headerbuf),
92
+ "POST %s HTTP/1.1\r\n"
93
+ /* "POST %s HTTP/1.0\r\n"*/
94
+ "Host: %s%s\r\n"
95
+ "User-Agent: POSIX, UPnP/1.0, miniUPnPc/1.0\r\n"
96
+ "Content-Length: %d\r\n"
97
+ "Content-Type: text/xml\r\n"
98
+ "SOAPAction: \"%s\"\r\n"
99
+ "Connection: Close\r\n"
100
+ "Cache-Control: no-cache\r\n" /* ??? */
101
+ "Pragma: no-cache\r\n"
102
+ "\r\n",
103
+ url, host, portstr, bodysize, action);
104
+ #ifdef DEBUG
105
+ printf("SOAP request : headersize=%d bodysize=%d\n",
106
+ headerssize, bodysize);
107
+ /*printf("%s", headerbuf);*/
108
+ #endif
109
+ return httpWrite(fd, body, bodysize, headerbuf, headerssize);
110
+ }
111
+
112
+
data/ext/minisoap.h ADDED
@@ -0,0 +1,15 @@
1
+ /* $Id: minisoap.h,v 1.3 2006/11/19 22:32:34 nanard Exp $ */
2
+ /* Project : miniupnp
3
+ * Author : Thomas Bernard
4
+ * Copyright (c) 2005 Thomas Bernard
5
+ * This software is subject to the conditions detailed in the
6
+ * LICENCE file provided in this distribution. */
7
+ #ifndef __MINISOAP_H__
8
+ #define __MINISOAP_H__
9
+
10
+ /*int httpWrite(int, const char *, int, const char *);*/
11
+ int soapPostSubmit(int, const char *, const char *, unsigned short,
12
+ const char *, const char *);
13
+
14
+ #endif
15
+
data/ext/minissdpc.c ADDED
@@ -0,0 +1,107 @@
1
+ /* $Id: minissdpc.c,v 1.4 2007/12/19 14:56:58 nanard Exp $ */
2
+ /* Project : miniupnp
3
+ * Author : Thomas BERNARD
4
+ * copyright (c) 2005-2007 Thomas Bernard
5
+ * This software is subjet to the conditions detailed in the
6
+ * provided LICENCE file. */
7
+ /*#include <syslog.h>*/
8
+ #include <stdio.h>
9
+ #include <string.h>
10
+ #include <stdlib.h>
11
+ #include <unistd.h>
12
+ #include <sys/types.h>
13
+ #include <sys/socket.h>
14
+ #include <sys/un.h>
15
+
16
+ #include "minissdpc.h"
17
+ #include "miniupnpc.h"
18
+
19
+ #define DECODELENGTH(n, p) n = 0; \
20
+ do { n = (n << 7) | (*p & 0x7f); } \
21
+ while(*(p++)&0x80);
22
+ #define CODELENGTH(n, p) do { *p = (n & 0x7f) | ((n > 0x7f) ? 0x80 : 0); \
23
+ p++; n >>= 7; } while(n);
24
+
25
+ struct UPNPDev *
26
+ getDevicesFromMiniSSDPD(const char * devtype, const char * socketpath)
27
+ {
28
+ struct UPNPDev * tmp;
29
+ struct UPNPDev * devlist = NULL;
30
+ unsigned char buffer[2048];
31
+ ssize_t n;
32
+ unsigned char * p;
33
+ unsigned char * url;
34
+ unsigned int i;
35
+ unsigned int urlsize, stsize, usnsize, l;
36
+ int s;
37
+ struct sockaddr_un addr;
38
+
39
+ s = socket(AF_UNIX, SOCK_STREAM, 0);
40
+ if(s < 0)
41
+ {
42
+ /*syslog(LOG_ERR, "socket(unix): %m");*/
43
+ perror("socket(unix)");
44
+ return NULL;
45
+ }
46
+ addr.sun_family = AF_UNIX;
47
+ strncpy(addr.sun_path, socketpath, sizeof(addr.sun_path));
48
+ if(connect(s, (struct sockaddr *)&addr, sizeof(struct sockaddr_un)) < 0)
49
+ {
50
+ /*syslog(LOG_WARNING, "connect(\"%s\"): %m", socketpath);*/
51
+ close(s);
52
+ return NULL;
53
+ }
54
+ stsize = strlen(devtype);
55
+ buffer[0] = 1;
56
+ p = buffer + 1;
57
+ l = stsize; CODELENGTH(l, p);
58
+ memcpy(p, devtype, stsize);
59
+ p += stsize;
60
+ if(write(s, buffer, p - buffer) < 0)
61
+ {
62
+ /*syslog(LOG_ERR, "write(): %m");*/
63
+ perror("minissdpc.c: write()");
64
+ close(s);
65
+ return NULL;
66
+ }
67
+ n = read(s, buffer, sizeof(buffer));
68
+ if(n<=0)
69
+ {
70
+ perror("minissdpc.c: read()");
71
+ close(s);
72
+ return NULL;
73
+ }
74
+ p = buffer + 1;
75
+ for(i = 0; i < buffer[0]; i++)
76
+ {
77
+ if(p+2>=buffer+sizeof(buffer))
78
+ break;
79
+ DECODELENGTH(urlsize, p);
80
+ if(p+urlsize+2>=buffer+sizeof(buffer))
81
+ break;
82
+ url = p;
83
+ p += urlsize;
84
+ DECODELENGTH(stsize, p);
85
+ if(p+stsize+2>=buffer+sizeof(buffer))
86
+ break;
87
+ tmp = (struct UPNPDev *)malloc(sizeof(struct UPNPDev)+urlsize+stsize);
88
+ tmp->pNext = devlist;
89
+ tmp->descURL = tmp->buffer;
90
+ tmp->st = tmp->buffer + 1 + urlsize;
91
+ memcpy(tmp->buffer, url, urlsize);
92
+ tmp->buffer[urlsize] = '\0';
93
+ memcpy(tmp->buffer + urlsize + 1, p, stsize);
94
+ p += stsize;
95
+ tmp->buffer[urlsize+1+stsize] = '\0';
96
+ devlist = tmp;
97
+ /* added for compatibility with recent versions of MiniSSDPd
98
+ * >= 2007/12/19 */
99
+ DECODELENGTH(usnsize, p);
100
+ p += usnsize;
101
+ if(p>buffer + sizeof(buffer))
102
+ break;
103
+ }
104
+ close(s);
105
+ return devlist;
106
+ }
107
+
data/ext/minissdpc.h ADDED
@@ -0,0 +1,15 @@
1
+ /* $Id: minissdpc.h,v 1.1 2007/08/31 15:15:33 nanard Exp $ */
2
+ /* Project: miniupnp
3
+ * http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/
4
+ * Author: Thomas Bernard
5
+ * Copyright (c) 2005-2007 Thomas Bernard
6
+ * This software is subjects to the conditions detailed
7
+ * in the LICENCE file provided within this distribution */
8
+ #ifndef __MINISSDPC_H__
9
+ #define __MINISSDPC_H__
10
+
11
+ struct UPNPDev *
12
+ getDevicesFromMiniSSDPD(const char * devtype, const char * socketpath);
13
+
14
+ #endif
15
+
data/ext/miniupnpc.c ADDED
@@ -0,0 +1,751 @@
1
+ /* $Id: miniupnpc.c,v 1.52 2008/02/18 13:28:33 nanard Exp $ */
2
+ /* Project : miniupnp
3
+ * Author : Thomas BERNARD
4
+ * copyright (c) 2005-2007 Thomas Bernard
5
+ * This software is subjet to the conditions detailed in the
6
+ * provided LICENCE file. */
7
+ #include <stdio.h>
8
+ #include <stdlib.h>
9
+ #include <string.h>
10
+ #ifdef WIN32
11
+ #include <winsock2.h>
12
+ #include <Ws2tcpip.h>
13
+ #include <io.h>
14
+ #define snprintf _snprintf
15
+ #define strncasecmp memicmp
16
+ #define MAXHOSTNAMELEN 64
17
+ #else
18
+ #include <unistd.h>
19
+ #include <sys/socket.h>
20
+ #include <sys/types.h>
21
+ #include <sys/param.h>
22
+ #include <netinet/in.h>
23
+ #include <arpa/inet.h>
24
+ #include <poll.h>
25
+ #include <netdb.h>
26
+ #define closesocket close
27
+ #endif
28
+ #include "miniupnpc.h"
29
+ #include "minissdpc.h"
30
+ #include "miniwget.h"
31
+ #include "minisoap.h"
32
+ #include "minixml.h"
33
+ #include "upnpcommands.h"
34
+
35
+ /* Uncomment the following to transmit the msearch from the same port
36
+ * as the UPnP multicast port. With WinXP this seems to result in the
37
+ * responses to the msearch being lost, thus if things dont work then
38
+ * comment this out. */
39
+ /* #define TX_FROM_UPNP_PORT */
40
+
41
+ #ifdef WIN32
42
+ #define PRINT_SOCKET_ERROR(x) printf("Socket error: %s, %d\n", x, WSAGetLastError());
43
+ #else
44
+ #define PRINT_SOCKET_ERROR(x) perror(x)
45
+ #endif
46
+
47
+ /* root description parsing */
48
+ void parserootdesc(const char * buffer, int bufsize, struct IGDdatas * data)
49
+ {
50
+ struct xmlparser parser;
51
+ /* xmlparser object */
52
+ parser.xmlstart = buffer;
53
+ parser.xmlsize = bufsize;
54
+ parser.data = data;
55
+ parser.starteltfunc = IGDstartelt;
56
+ parser.endeltfunc = IGDendelt;
57
+ parser.datafunc = IGDdata;
58
+ parser.attfunc = 0;
59
+ parsexml(&parser);
60
+ #ifndef NDEBUG
61
+ printIGD(data);
62
+ #endif
63
+ }
64
+
65
+ /* Content-length: nnn */
66
+ static int getcontentlenfromline(const char * p, int n)
67
+ {
68
+ static const char contlenstr[] = "content-length";
69
+ const char * p2 = contlenstr;
70
+ int a = 0;
71
+ while(*p2)
72
+ {
73
+ if(n==0)
74
+ return -1;
75
+ if(*p2 != *p && *p2 != (*p + 32))
76
+ return -1;
77
+ p++; p2++; n--;
78
+ }
79
+ if(n==0)
80
+ return -1;
81
+ if(*p != ':')
82
+ return -1;
83
+ p++; n--;
84
+ while(*p == ' ')
85
+ {
86
+ if(n==0)
87
+ return -1;
88
+ p++; n--;
89
+ }
90
+ while(*p >= '0' && *p <= '9')
91
+ {
92
+ if(n==0)
93
+ return -1;
94
+ a = (a * 10) + (*p - '0');
95
+ p++; n--;
96
+ }
97
+ return a;
98
+ }
99
+
100
+ static void
101
+ getContentLengthAndHeaderLength(char * p, int n,
102
+ int * contentlen, int * headerlen)
103
+ {
104
+ char * line;
105
+ int linelen;
106
+ int r;
107
+ line = p;
108
+ while(line < p + n)
109
+ {
110
+ linelen = 0;
111
+ while(line[linelen] != '\r' && line[linelen] != '\r')
112
+ {
113
+ if(line+linelen >= p+n)
114
+ return;
115
+ linelen++;
116
+ }
117
+ r = getcontentlenfromline(line, linelen);
118
+ if(r>0)
119
+ *contentlen = r;
120
+ line = line + linelen + 2;
121
+ if(line[0] == '\r' && line[1] == '\n')
122
+ {
123
+ *headerlen = (line - p) + 2;
124
+ return;
125
+ }
126
+ }
127
+ }
128
+
129
+ /* simpleUPnPcommand :
130
+ * not so simple !
131
+ * return values :
132
+ * 0 - OK
133
+ * -1 - error */
134
+ int simpleUPnPcommand(int s, const char * url, const char * service,
135
+ const char * action, struct UPNParg * args,
136
+ char * buffer, int * bufsize)
137
+ {
138
+ struct sockaddr_in dest;
139
+ char hostname[MAXHOSTNAMELEN+1];
140
+ unsigned short port = 0;
141
+ char * path;
142
+ char soapact[128];
143
+ char soapbody[2048];
144
+ int soapbodylen;
145
+ char * buf;
146
+ int buffree;
147
+ int n;
148
+ int contentlen, headerlen; /* for the response */
149
+ snprintf(soapact, sizeof(soapact), "%s#%s", service, action);
150
+ if(args==NULL)
151
+ {
152
+ /*soapbodylen = snprintf(soapbody, sizeof(soapbody),
153
+ "<?xml version=\"1.0\"?>\r\n"
154
+ "<SOAP-ENV:Envelope "
155
+ "xmlns:SOAP-ENV=\"http://schemas.xmlsoap.org/soap/envelope/\" "
156
+ "SOAP-ENV:encodingStyle=\"http://schemas.xmlsoap.org/soap/encoding/\">"
157
+ "<SOAP-ENV:Body>"
158
+ "<m:%s xmlns:m=\"%s\"/>"
159
+ "</SOAP-ENV:Body></SOAP-ENV:Envelope>"
160
+ "\r\n", action, service);*/
161
+ soapbodylen = snprintf(soapbody, sizeof(soapbody),
162
+ "<?xml version=\"1.0\"?>\r\n"
163
+ "<s:Envelope "
164
+ "xmlns:s=\"http://schemas.xmlsoap.org/soap/envelope/\" "
165
+ "s:encodingStyle=\"http://schemas.xmlsoap.org/soap/encoding/\">"
166
+ "<s:Body>"
167
+ "<m:%s xmlns:m=\"%s\">"
168
+ "</m:%s>"
169
+ "</s:Body></s:Envelope>"
170
+ "\r\n", action, service, action);
171
+ }
172
+ else
173
+ {
174
+ char * p;
175
+ const char * pe, * pv;
176
+ soapbodylen = snprintf(soapbody, sizeof(soapbody),
177
+ "<?xml version=\"1.0\"?>\r\n"
178
+ "<SOAP-ENV:Envelope "
179
+ "xmlns:SOAP-ENV=\"http://schemas.xmlsoap.org/soap/envelope/\" "
180
+ "SOAP-ENV:encodingStyle=\"http://schemas.xmlsoap.org/soap/encoding/\">"
181
+ "<SOAP-ENV:Body>"
182
+ "<m:%s xmlns:m=\"%s\">",
183
+ action, service);
184
+ p = soapbody + soapbodylen;
185
+ while(args->elt)
186
+ {
187
+ /* check that we are never overflowing the string... */
188
+ if(soapbody + sizeof(soapbody) <= p + 100)
189
+ {
190
+ /* we keep a margin of at least 100 bytes */
191
+ *bufsize = 0;
192
+ return -1;
193
+ }
194
+ *(p++) = '<';
195
+ pe = args->elt;
196
+ while(*pe)
197
+ *(p++) = *(pe++);
198
+ *(p++) = '>';
199
+ if((pv = args->val))
200
+ {
201
+ while(*pv)
202
+ *(p++) = *(pv++);
203
+ }
204
+ *(p++) = '<';
205
+ *(p++) = '/';
206
+ pe = args->elt;
207
+ while(*pe)
208
+ *(p++) = *(pe++);
209
+ *(p++) = '>';
210
+ args++;
211
+ }
212
+ *(p++) = '<';
213
+ *(p++) = '/';
214
+ *(p++) = 'm';
215
+ *(p++) = ':';
216
+ pe = action;
217
+ while(*pe)
218
+ *(p++) = *(pe++);
219
+ strncpy(p, "></SOAP-ENV:Body></SOAP-ENV:Envelope>\r\n",
220
+ soapbody + sizeof(soapbody) - p);
221
+ }
222
+ if(!parseURL(url, hostname, &port, &path)) return -1;
223
+ if(s<0)
224
+ {
225
+ s = socket(PF_INET, SOCK_STREAM, 0);
226
+ if(s<0)
227
+ {
228
+ PRINT_SOCKET_ERROR("socket");
229
+ *bufsize = 0;
230
+ return -1;
231
+ }
232
+ dest.sin_family = AF_INET;
233
+ dest.sin_port = htons(port);
234
+ dest.sin_addr.s_addr = inet_addr(hostname);
235
+ if(connect(s, (struct sockaddr *)&dest, sizeof(struct sockaddr))<0)
236
+ {
237
+ PRINT_SOCKET_ERROR("connect");
238
+ closesocket(s);
239
+ *bufsize = 0;
240
+ return -1;
241
+ }
242
+ }
243
+
244
+ n = soapPostSubmit(s, path, hostname, port, soapact, soapbody);
245
+ if(n<=0) {
246
+ #ifdef DEBUG
247
+ printf("Error sending SOAP request\n");
248
+ #endif
249
+ closesocket(s);
250
+ return -1;
251
+ }
252
+
253
+ contentlen = -1;
254
+ headerlen = -1;
255
+ buf = buffer;
256
+ buffree = *bufsize;
257
+ *bufsize = 0;
258
+ while ((n = ReceiveData(s, buf, buffree, 5000)) > 0) {
259
+ buffree -= n;
260
+ buf += n;
261
+ *bufsize += n;
262
+ getContentLengthAndHeaderLength(buffer, *bufsize,
263
+ &contentlen, &headerlen);
264
+ #ifdef DEBUG
265
+ printf("received n=%dbytes bufsize=%d ContLen=%d HeadLen=%d\n",
266
+ n, *bufsize, contentlen, headerlen);
267
+ #endif
268
+ /* break if we received everything */
269
+ if(contentlen > 0 && headerlen > 0 && *bufsize >= contentlen+headerlen)
270
+ break;
271
+ }
272
+
273
+ closesocket(s);
274
+ return 0;
275
+ }
276
+
277
+ /* parseMSEARCHReply()
278
+ * the last 4 arguments are filled during the parsing :
279
+ * - location/locationsize : "location:" field of the SSDP reply packet
280
+ * - st/stsize : "st:" field of the SSDP reply packet.
281
+ * The strings are NOT null terminated */
282
+ static void
283
+ parseMSEARCHReply(const char * reply, int size,
284
+ const char * * location, int * locationsize,
285
+ const char * * st, int * stsize)
286
+ {
287
+ int a, b, i;
288
+ i = 0;
289
+ a = i; /* start of the line */
290
+ b = 0;
291
+ while(i<size)
292
+ {
293
+ switch(reply[i])
294
+ {
295
+ case ':':
296
+ if(b==0)
297
+ {
298
+ b = i; /* end of the "header" */
299
+ /*for(j=a; j<b; j++)
300
+ {
301
+ putchar(reply[j]);
302
+ }
303
+ */
304
+ }
305
+ break;
306
+ case '\x0a':
307
+ case '\x0d':
308
+ if(b!=0)
309
+ {
310
+ /*for(j=b+1; j<i; j++)
311
+ {
312
+ putchar(reply[j]);
313
+ }
314
+ putchar('\n');*/
315
+ do { b++; } while(reply[b]==' ');
316
+ if(0==strncasecmp(reply+a, "location", 8))
317
+ {
318
+ *location = reply+b;
319
+ *locationsize = i-b;
320
+ }
321
+ else if(0==strncasecmp(reply+a, "st", 2))
322
+ {
323
+ *st = reply+b;
324
+ *stsize = i-b;
325
+ }
326
+ b = 0;
327
+ }
328
+ a = i+1;
329
+ break;
330
+ default:
331
+ break;
332
+ }
333
+ i++;
334
+ }
335
+ }
336
+
337
+ /* port upnp discover : SSDP protocol */
338
+ #define PORT (1900)
339
+ #define UPNP_MCAST_ADDR "239.255.255.250"
340
+
341
+ /* upnpDiscover() :
342
+ * return a chained list of all devices found or NULL if
343
+ * no devices was found.
344
+ * It is up to the caller to free the chained list
345
+ * delay is in millisecond (poll) */
346
+ struct UPNPDev * upnpDiscover(int delay, const char * multicastif,
347
+ const char * minissdpdsock)
348
+ {
349
+ struct UPNPDev * tmp;
350
+ struct UPNPDev * devlist = 0;
351
+ int opt = 1;
352
+ static const char MSearchMsgFmt[] =
353
+ "M-SEARCH * HTTP/1.1\r\n"
354
+ "HOST: " UPNP_MCAST_ADDR ":" "1900" "\r\n"
355
+ "ST: %s\r\n"
356
+ "MAN: \"ssdp:discover\"\r\n"
357
+ "MX: 3\r\n"
358
+ "\r\n";
359
+ static const char * const deviceList[] = {
360
+ "urn:schemas-upnp-org:device:InternetGatewayDevice:1",
361
+ "urn:schemas-upnp-org:service:WANIPConnection:1",
362
+ "urn:schemas-upnp-org:service:WANPPPConnection:1",
363
+ "upnp:rootdevice",
364
+ 0
365
+ };
366
+ int deviceIndex = 0;
367
+ char bufr[1536]; /* reception and emission buffer */
368
+ int sudp;
369
+ int n;
370
+ struct sockaddr_in sockudp_r, sockudp_w;
371
+
372
+ #ifndef WIN32
373
+ /* first try to get infos from minissdpd ! */
374
+ if(!minissdpdsock)
375
+ minissdpdsock = "/var/run/minissdpd.sock";
376
+ while(!devlist && deviceList[deviceIndex]) {
377
+ devlist = getDevicesFromMiniSSDPD(deviceList[deviceIndex],
378
+ minissdpdsock);
379
+ /* We return what we have found if it was not only a rootdevice */
380
+ if(devlist && !strstr(deviceList[deviceIndex], "rootdevice"))
381
+ return devlist;
382
+ deviceIndex++;
383
+ }
384
+ deviceIndex = 0;
385
+ #endif
386
+ /* fallback to direct discovery */
387
+ #ifdef WIN32
388
+ sudp = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP);
389
+ #else
390
+ sudp = socket(PF_INET, SOCK_DGRAM, 0);
391
+ #endif
392
+ if(sudp < 0)
393
+ {
394
+ PRINT_SOCKET_ERROR("socket");
395
+ return NULL;
396
+ }
397
+ /* reception */
398
+ memset(&sockudp_r, 0, sizeof(struct sockaddr_in));
399
+ sockudp_r.sin_family = AF_INET;
400
+ #ifdef TX_FROM_UPNP_PORT
401
+ sockudp_r.sin_port = htons(PORT);
402
+ #endif
403
+ sockudp_r.sin_addr.s_addr = INADDR_ANY;
404
+ /* emission */
405
+ memset(&sockudp_w, 0, sizeof(struct sockaddr_in));
406
+ sockudp_w.sin_family = AF_INET;
407
+ sockudp_w.sin_port = htons(PORT);
408
+ sockudp_w.sin_addr.s_addr = inet_addr(UPNP_MCAST_ADDR);
409
+
410
+ #ifdef WIN32
411
+ if (setsockopt(sudp, SOL_SOCKET, SO_REUSEADDR, (const char *)&opt, sizeof (opt)) < 0)
412
+ #else
413
+ if (setsockopt(sudp, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof (opt)) < 0)
414
+ #endif
415
+ {
416
+ PRINT_SOCKET_ERROR("setsockopt");
417
+ return NULL;
418
+ }
419
+
420
+ if(multicastif)
421
+ {
422
+ struct in_addr mc_if;
423
+ mc_if.s_addr = inet_addr(multicastif);
424
+ sockudp_r.sin_addr.s_addr = mc_if.s_addr;
425
+ if(setsockopt(sudp, IPPROTO_IP, IP_MULTICAST_IF, (const char *)&mc_if, sizeof(mc_if)) < 0)
426
+ {
427
+ PRINT_SOCKET_ERROR("setsockopt");
428
+ }
429
+ }
430
+
431
+ /* Avant d'envoyer le paquet on bind pour recevoir la reponse */
432
+ if (bind(sudp, (struct sockaddr *)&sockudp_r, sizeof(struct sockaddr_in)) != 0)
433
+ {
434
+ PRINT_SOCKET_ERROR("bind");
435
+ closesocket(sudp);
436
+ return NULL;
437
+ }
438
+
439
+ /* receiving SSDP response packet */
440
+ for(n = 0;;)
441
+ {
442
+ if(n == 0)
443
+ {
444
+ /* sending the SSDP M-SEARCH packet */
445
+ n = snprintf(bufr, sizeof(bufr),
446
+ MSearchMsgFmt, deviceList[deviceIndex++]);
447
+ /*printf("Sending %s", bufr);*/
448
+ n = sendto(sudp, bufr, n, 0,
449
+ (struct sockaddr *)&sockudp_w, sizeof(struct sockaddr_in));
450
+ if (n < 0) {
451
+ PRINT_SOCKET_ERROR("sendto");
452
+ closesocket(sudp);
453
+ return devlist;
454
+ }
455
+ }
456
+ /* Waiting for SSDP REPLY packet to M-SEARCH */
457
+ n = ReceiveData(sudp, bufr, sizeof(bufr), delay);
458
+ if (n < 0) {
459
+ /* error */
460
+ closesocket(sudp);
461
+ return devlist;
462
+ } else if (n == 0) {
463
+ /* no data or Time Out */
464
+ if (devlist || (deviceList[deviceIndex] == 0)) {
465
+ /* no more device type to look for... */
466
+ closesocket(sudp);
467
+ return devlist;
468
+ }
469
+ } else {
470
+ const char * descURL=NULL;
471
+ int urlsize=0;
472
+ const char * st=NULL;
473
+ int stsize=0;
474
+ /*printf("%d byte(s) :\n%s\n", n, bufr);*/ /* affichage du message */
475
+ parseMSEARCHReply(bufr, n, &descURL, &urlsize, &st, &stsize);
476
+ if(st&&descURL)
477
+ {
478
+ /*printf("M-SEARCH Reply:\nST: %.*s\nLocation: %.*s\n",
479
+ stsize, st, urlsize, descURL); */
480
+ tmp = (struct UPNPDev *)malloc(sizeof(struct UPNPDev)+urlsize+stsize);
481
+ tmp->pNext = devlist;
482
+ tmp->descURL = tmp->buffer;
483
+ tmp->st = tmp->buffer + 1 + urlsize;
484
+ memcpy(tmp->buffer, descURL, urlsize);
485
+ tmp->buffer[urlsize] = '\0';
486
+ memcpy(tmp->buffer + urlsize + 1, st, stsize);
487
+ tmp->buffer[urlsize+1+stsize] = '\0';
488
+ devlist = tmp;
489
+ }
490
+ }
491
+ }
492
+ }
493
+
494
+ /* freeUPNPDevlist() should be used to
495
+ * free the chained list returned by upnpDiscover() */
496
+ void freeUPNPDevlist(struct UPNPDev * devlist)
497
+ {
498
+ struct UPNPDev * next;
499
+ while(devlist)
500
+ {
501
+ next = devlist->pNext;
502
+ free(devlist);
503
+ devlist = next;
504
+ }
505
+ }
506
+
507
+ static void
508
+ url_cpy_or_cat(char * dst, const char * src, int n)
509
+ {
510
+ if( (src[0] == 'h')
511
+ &&(src[1] == 't')
512
+ &&(src[2] == 't')
513
+ &&(src[3] == 'p')
514
+ &&(src[4] == ':')
515
+ &&(src[5] == '/')
516
+ &&(src[6] == '/'))
517
+ {
518
+ strncpy(dst, src, n);
519
+ }
520
+ else
521
+ {
522
+ int l = strlen(dst);
523
+ if(src[0] != '/')
524
+ dst[l++] = '/';
525
+ if(l<=n)
526
+ strncpy(dst + l, src, n - l);
527
+ }
528
+ }
529
+
530
+ /* Prepare the Urls for usage...
531
+ */
532
+ void GetUPNPUrls(struct UPNPUrls * urls, struct IGDdatas * data,
533
+ const char * descURL)
534
+ {
535
+ char * p;
536
+ int n1, n2, n3;
537
+ n1 = strlen(data->urlbase);
538
+ if(n1==0)
539
+ n1 = strlen(descURL);
540
+ n1 += 2; /* 1 byte more for Null terminator, 1 byte for '/' if needed */
541
+ n2 = n1; n3 = n1;
542
+ n1 += strlen(data->scpdurl);
543
+ n2 += strlen(data->controlurl);
544
+ n3 += strlen(data->controlurl_CIF);
545
+
546
+ urls->ipcondescURL = (char *)malloc(n1);
547
+ urls->controlURL = (char *)malloc(n2);
548
+ urls->controlURL_CIF = (char *)malloc(n3);
549
+ /* maintenant on chope la desc du WANIPConnection */
550
+ if(data->urlbase[0] != '\0')
551
+ strncpy(urls->ipcondescURL, data->urlbase, n1);
552
+ else
553
+ strncpy(urls->ipcondescURL, descURL, n1);
554
+ p = strchr(urls->ipcondescURL+7, '/');
555
+ if(p) p[0] = '\0';
556
+ strncpy(urls->controlURL, urls->ipcondescURL, n2);
557
+ strncpy(urls->controlURL_CIF, urls->ipcondescURL, n3);
558
+
559
+ url_cpy_or_cat(urls->ipcondescURL, data->scpdurl, n1);
560
+
561
+ url_cpy_or_cat(urls->controlURL, data->controlurl, n2);
562
+
563
+ url_cpy_or_cat(urls->controlURL_CIF, data->controlurl_CIF, n3);
564
+
565
+ #ifdef DEBUG
566
+ printf("urls->ipcondescURL='%s' %d n1=%d\n", urls->ipcondescURL,
567
+ strlen(urls->ipcondescURL), n1);
568
+ printf("urls->controlURL='%s' %d n2=%d\n", urls->controlURL,
569
+ strlen(urls->controlURL), n2);
570
+ printf("urls->controlURL_CIF='%s' %d n3=%d\n", urls->controlURL_CIF,
571
+ strlen(urls->controlURL_CIF), n3);
572
+ #endif
573
+ }
574
+
575
+ void
576
+ FreeUPNPUrls(struct UPNPUrls * urls)
577
+ {
578
+ if(!urls)
579
+ return;
580
+ free(urls->controlURL);
581
+ urls->controlURL = 0;
582
+ free(urls->ipcondescURL);
583
+ urls->ipcondescURL = 0;
584
+ free(urls->controlURL_CIF);
585
+ urls->controlURL_CIF = 0;
586
+ }
587
+
588
+
589
+ int ReceiveData(int socket, char * data, int length, int timeout)
590
+ {
591
+ int n;
592
+ #ifndef WIN32
593
+ struct pollfd fds[1]; /* for the poll */
594
+ fds[0].fd = socket;
595
+ fds[0].events = POLLIN;
596
+ n = poll(fds, 1, timeout);
597
+ if(n < 0)
598
+ {
599
+ PRINT_SOCKET_ERROR("poll");
600
+ return -1;
601
+ }
602
+ else if(n == 0)
603
+ {
604
+ return 0;
605
+ }
606
+ #else
607
+ fd_set socketSet;
608
+ TIMEVAL timeval;
609
+ FD_ZERO(&socketSet);
610
+ FD_SET(socket, &socketSet);
611
+ timeval.tv_sec = timeout / 1000;
612
+ timeval.tv_usec = (timeout % 1000) * 1000;
613
+ /*n = select(0, &socketSet, NULL, NULL, &timeval);*/
614
+ n = select(FD_SETSIZE, &socketSet, NULL, NULL, &timeval);
615
+ if(n < 0)
616
+ {
617
+ PRINT_SOCKET_ERROR("select");
618
+ return -1;
619
+ }
620
+ else if(n == 0)
621
+ {
622
+ return 0;
623
+ }
624
+ #endif
625
+ n = recv(socket, data, length, 0);
626
+ if(n<0)
627
+ {
628
+ PRINT_SOCKET_ERROR("recv");
629
+ }
630
+ return n;
631
+ }
632
+
633
+ int
634
+ UPNPIGD_IsConnected(struct UPNPUrls * urls, struct IGDdatas * data)
635
+ {
636
+ char status[64];
637
+ unsigned int uptime;
638
+ status[0] = '\0';
639
+ UPNP_GetStatusInfo(urls->controlURL, data->servicetype,
640
+ status, &uptime, NULL);
641
+ if(0 == strcmp("Connected", status))
642
+ {
643
+ return 1;
644
+ }
645
+ else
646
+ return 0;
647
+ }
648
+
649
+
650
+ /* UPNP_GetValidIGD() :
651
+ * return values :
652
+ * 0 = NO IGD found
653
+ * 1 = A valid connected IGD has been found
654
+ * 2 = A valid IGD has been found but it reported as
655
+ * not connected
656
+ * 3 = an UPnP device has been found but was not recognized as an IGD
657
+ *
658
+ * In any non zero return case, the urls and data structures
659
+ * passed as parameters are set. Donc forget to call FreeUPNPUrls(urls) to
660
+ * free allocated memory.
661
+ */
662
+ int
663
+ UPNP_GetValidIGD(struct UPNPDev * devlist,
664
+ struct UPNPUrls * urls,
665
+ struct IGDdatas * data,
666
+ char * lanaddr, int lanaddrlen)
667
+ {
668
+ char * descXML;
669
+ int descXMLsize = 0;
670
+ struct UPNPDev * dev;
671
+ int ndev = 0;
672
+ int state; /* state 1 : IGD connected. State 2 : IGD. State 3 : anything */
673
+ if(!devlist)
674
+ {
675
+ #ifdef DEBUG
676
+ printf("Empty devlist\n");
677
+ #endif
678
+ return 0;
679
+ }
680
+ for(state = 1; state <= 3; state++)
681
+ {
682
+ for(dev = devlist; dev; dev = dev->pNext)
683
+ {
684
+ /* we should choose an internet gateway device.
685
+ * with st == urn:schemas-upnp-org:device:InternetGatewayDevice:1 */
686
+ descXML = miniwget_getaddr(dev->descURL, &descXMLsize,
687
+ lanaddr, lanaddrlen);
688
+ if(descXML)
689
+ {
690
+ ndev++;
691
+ memset(data, 0, sizeof(struct IGDdatas));
692
+ memset(urls, 0, sizeof(struct UPNPUrls));
693
+ parserootdesc(descXML, descXMLsize, data);
694
+ free(descXML);
695
+ descXML = NULL;
696
+ if(0==strcmp(data->servicetype_CIF,
697
+ "urn:schemas-upnp-org:service:WANCommonInterfaceConfig:1")
698
+ || state >= 3 )
699
+ {
700
+ GetUPNPUrls(urls, data, dev->descURL);
701
+
702
+ #ifdef DEBUG
703
+ printf("UPNPIGD_IsConnected(%s) = %d\n",
704
+ urls->controlURL,
705
+ UPNPIGD_IsConnected(urls, data));
706
+ #endif
707
+ if((state >= 2) || UPNPIGD_IsConnected(urls, data))
708
+ return state;
709
+ FreeUPNPUrls(urls);
710
+ }
711
+ memset(data, 0, sizeof(struct IGDdatas));
712
+ }
713
+ #ifdef DEBUG
714
+ else
715
+ {
716
+ printf("error getting XML description %s\n", dev->descURL);
717
+ }
718
+ #endif
719
+ }
720
+ }
721
+ return 0;
722
+ }
723
+
724
+ /* UPNP_GetIGDFromUrl()
725
+ * Used when skipping the discovery process.
726
+ * return value :
727
+ * 0 - Not ok
728
+ * 1 - OK */
729
+ int
730
+ UPNP_GetIGDFromUrl(const char * rootdescurl,
731
+ struct UPNPUrls * urls,
732
+ struct IGDdatas * data,
733
+ char * lanaddr, int lanaddrlen)
734
+ {
735
+ char * descXML;
736
+ int descXMLsize = 0;
737
+ descXML = miniwget_getaddr(rootdescurl, &descXMLsize,
738
+ lanaddr, lanaddrlen);
739
+ if(descXML) {
740
+ memset(data, 0, sizeof(struct IGDdatas));
741
+ memset(urls, 0, sizeof(struct UPNPUrls));
742
+ parserootdesc(descXML, descXMLsize, data);
743
+ free(descXML);
744
+ descXML = NULL;
745
+ GetUPNPUrls(urls, data, rootdescurl);
746
+ return 1;
747
+ } else {
748
+ return 0;
749
+ }
750
+ }
751
+